在 java 中使用 try-catch over if 条件安全地设置对性能影响最小的值



在这里,我的主要目标是安全地设置值,而不会对性能(速度、内存、CPU 等)产生影响。

我有一个愚蠢的选择(风格不好),下面也提到过。 那么,最好的方法是什么?选项 1?选项 2?还是另一个?

选项 1 :

if(
animalData!=null && 
animalData.getBreedData()!=null && 
dogx.getBreed() != null && dogx.getBreed().getBreedCode() != null && 
animalData.getBreedData().get(dogx.getBreed().getBreedCode()) != null
){
dogx.getBreed().setBreedId(animalData.getBreedData().get(dogx.getBreed().getBreedCode()));
}

选项 2 :

try{dogx.getBreed().setBreedId(animalData.getBreedData().get(dogx.getBreed().getBreedCode()));}catch(Exception e){}

注意:这段代码处于一个有数千次迭代的循环中。

检查nulls 是唯一符合 Java 异常理念的选项。

NullPointerException是一个RuntimeException,这意味着它是为报告编程错误而设计的。这使得将其用于终止程序以外的任何操作都是一种极其糟糕的做法。

您可以通过存储对对象的引用来优化代码以进行null检查,以便以后可以重用它们:

BreedData breedData;
DogBreed dogBreed;
String breedCode;
String breedId;
if( animalData != null
&&  (breedData = animalData.getBreedData())!=null
&&  (dogBreed = dogx.getBreed()) != null
&&  (breedCode = dogx.getBreed().getBreedCode()) != null
&&  (breedId = breedData.get(breedCode)) != null
) {
dogBreed.setBreedId(breedId);
}

选项 3:

Optional.ofNullable(animalData)
.map(animalData -> animalData.getBreedData())
.ifPresent(breedData -> {
Optional.ofNullable(dogx.getBreed())
.map(breed -> breed.getBreedCode())
.ifPresent(breedCode -> {
thisBreedData = breedData.get(breedCode); // here we could use a third Optional call…
if (thisBreedData != null) {
dogx.getBreed().setBreedId(thisBreedData));
}
}) 
});
}

上面的答案实际上并没有回答你的问题。所以这是我的:

如果性能对您来说真的很重要 - 删除null检查可能是一个好主意。许多高性能库都使用这种方法,例如,这里是来自HikariCP(最快的java DB连接池)的FastList类的代码:

public boolean add(T element)
{
try {
elementData[size++] = element;
} catch (ArrayIndexOutOfBoundsException e) {
...
}
}

添加此尝试捕获作为范围检查的替换。删除边界检查实际上使此代码更快。

以下是证明这一点的基准:

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS, batchSize = 1000)
@Measurement(iterations = 20, time = 1, timeUnit = TimeUnit.SECONDS, batchSize = 1000)
public class BoundsCheck {
@Param("1")
int index;
int[] ar;
@Setup
public void setup() {
ar = new int[] {1, 2};
}
@Benchmark
public int boundCheck() {
if (index >= ar.length) {
throw new IndexOutOfBoundsException();
}
return ar[index];
}
@Benchmark
public int noCheck() {
return ar[index];
}
@Benchmark
public int noCheckWithTryCatch() {
try {
return ar[index];
} catch (RuntimeException e) {
return -1;
}
}
}

结果:

Benchmark                        (index)   Mode  Cnt       Score       Error  Units
BoundsCheck.boundCheck                 1  thrpt   20  334197.761 ±  2593.034  ops/s
BoundsCheck.noCheck                    1  thrpt   20  370148.527 ±  9016.179  ops/s
BoundsCheck.noCheckWithTryCatch        1  thrpt   20  371782.744 ± 17290.347  ops/s

我们能在这里看到什么?消除边界检查可为您带来 +10% 的性能提升。 在引发异常之前,try {} catch不收取任何费用。

但是,此方法仅适用于可以保证数据中没有 NPE 的情况。因此,异常实际上永远不会被抛出或很少抛出。否则,异常引发可能会使代码变慢。

这里没有确切的答案。这实际上取决于您的数据,您需要了解您的数据才能得出任何进一步的结论。

另外,请记住,JIT 可以尽可能消除代码中的null检查,因此这种优化可能不值得。只有使用真实数据的测试才能给你答案。

最新更新