哪种更好,更便宜:类匹配还是异常?

3
哪个更便宜和更好:put1还是put2?
Map<String, Animal> map = new Map<String, Animal>();

void put1(){
 for (.....)
  if (Animal.class.isAssignableFrom(item[i].getClass())
   map.put(key[i], item[i]);

void put2(){
 for (.....)
  try{
   map.put(key[i], item[i]);}
  catch (...){}
问题修订: 这个问题不是很清楚。让我稍微修改一下问题。我忘记了转换,所以put2依赖于转换异常失败。isAssignableFrom()、isInstanceOf()和instanceof在功能上相似,因此产生相同的开销,只是第一个方法包括子类,而第二个方法用于精确类型匹配,第三个是操作符版本。反射方法和异常都是昂贵的操作。

我的问题是针对那些在这个领域做过一些基准测试的人——instanceof/isassignablefrom与转换异常哪个更便宜、更可取?

void put1(){
 for (.....)
  if (Animal.class.isAssignableFrom(item[i].getClass())
   map.put(key[i], (Animal)item[i]);

void put2(){
 for (.....)
  try{
   map.put(key[i], (Animal)item[i]);}
  catch (...){}

1
听起来你过于高估了原始CPU速度的问题。你应该更关注什么是正确的,清晰的,可读的和惯用的Java代码。只有在极少数情况下,你才会想要篡改它以削减一些时钟周期。 - Kevin Bourrillion
5个回答

12

可能你想要:

if (item[i] instanceof Animal)
    map.put(key[i], (Animal) item[i]);

这几乎肯定比调用isAssignableFrom方法要好。

或者在C#中(因为您添加了C#标签):

var a = item[i] as Animal;
if (a != null)
    map[key[i]] = a;

编辑:更新后的问题是,哪个更好:instanceof 还是 强制类型转换加异常捕获。其功能基本相同。性能差异可能不显著,需要测量;一般来说,抛出异常会比较慢,但我不确定其他方面。因此,我将根据代码风格进行决策。表达清楚你的意思。

如果您始终期望item[i]是一个Animal,并且只是额外小心,那么请使用强制转换和捕获异常。否则,我发现使用 instanceof 更清晰明了,因为它明确地表明了你的意思:“如果这个对象是一个 Animal,则将其放入 map 中”。


1
以下代码也适用于 C#(仅供您与 Java 示例进行比较): if (item[i] is Animal) map[key[i]] = (Animal) item[i]; - David Hall
哦,顺便加一分,回答得很好 :) - David Hall
1
这是合法的 C# 代码,但使用 as 更优。 - Joren
最好使用if进行检查,因为:1.您的FOR循环将执行较少的map.put(),异常版本将在不满足条件时最多调用map.put(),每次都会产生异常。2.创建异常的过程是昂贵的,它占用堆栈上的内存,产生CPU注册表中的更改等,无论如何都比简单的if要复杂得多。 - ante.sabo
请根据我的问题修订您的答案。问题不是关于instanceof vs isassignablefrom,而是关于instanceof/isassignablefrom vs cast exception。 - Blessed Geek
这个答案中的第二个建议比你提出的任何一个选项都要好。 - Adam Ralph

2

我有点困惑。如果item[i]不是一个Animal,那么map.put(key[i], item[i])怎么会编译通过呢?

话虽如此,第一种方法表达了你的意图,但我认为使用instanceof进行检查会更好。


1

我同意之前的回答 - 这段代码无法编译。

但是,在我看来,它是一个异常还是检查取决于函数的目的。

item[i] 不是 Animal 是否是错误/异常情况?这种情况是否预计很少发生?如果是这样,那么应该使用异常。

如果它是逻辑的一部分 - 意味着您希望 item[i] 是许多东西 - 并且只有当它是 Animal 时才想要放入映射中。在这种情况下,instanceof 检查是正确的方法。

更新:

我还会添加一个例子(有点简单):

哪个更好:

(1)

if ( aNumber < 100 ) {   
 processNumber(aNumber); 
}

或者(2)

try {
    processNumber(aNumber); //Throws exception if aNumber >= 100
} catch () {
}

这取决于程序的功能。如果输入是整数,(1) 可用于计算小于100的数字。如果 processNumber 函数期望一个百分比值,且该值不能大于100,则会使用 (2)。

区别在于,对于程序 (2),aNumber 大于100 是错误的。然而,对于程序 (1),aNumber 大于100 是有效的,但只有当 aNumber 小于100 时才会发生 "某些" 事情。

PS - 这可能对您没有任何帮助,如果是这种情况,我很抱歉。


请根据我的问题修订您的答案。问题是instanceof/isassignablefrom与cast exception。 - Blessed Geek

1

通常异常处理会明显变慢,因为它被认为是用于异常情况(很少发生),虚拟机制造商没有花费太多精力加快其速度。

我认为你这个代码使用try/catch是异常处理的滥用,我永远不会考虑这样做。你想这样做的事实可能意味着你的设计有问题,items应该是Animal[]而不是其他类型,在这种情况下,您根本不需要在运行时进行检查。让编译器为您完成工作。


0

你的两个选择并不完全相等。选择哪一个完全取决于你的代码要做什么:

  • 如果该项始终应该是一个Animal,那么你应该使用put2(如果不是这种情况,它会抛出异常...)
  • 如果该项可能是或可能不是一个Animal,则应该使用put1(它检查条件而不是错误...)

如果你正在编写代码,首先不要关心性能!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接