如何正确比较Java中的两个整数?

我知道如果你把一个盒装的原始Integer与一个常量比较,如:

Integer a = 4; if (a < 5) 

a将自动被拆箱,比较将工作。

然而,当你比较两个盒装Integers并想要比较相等或小于/大于?

 Integer a = 4; Integer b = 5; if (a == b) 

上面的代码会导致检查,看看他们是否是相同的对象,或者它会自动取消箱子在这种情况下?

关于什么:

 Integer a = 4; Integer b = 5; if (a < b) 

不,==之间的整数,长等将检查参考相等 – 即

 Integer x = ...; Integer y = ...; System.out.println(x == y); 

这将检查xy是否指向相同的对象而不是相等的对象。

所以

 Integer x = new Integer(10); Integer y = new Integer(10); System.out.println(x == y); 

保证打印false 。 “小”自动拷贝值的实习可能会导致棘手的结果:

 Integer x = 10; Integer y = 10; System.out.println(x == y); 

由于拳击规则( JLS第5.1.7节 ),这将打印true 。 它仍然是参考平等正在使用,但参考真正平等的。

我个人会使用:

 if (x.intValue() == y.intValue()) 

要么

 if (x.equals(y)) 

后者的效率稍低一点 – Integer.equals(Integer)没有重载,所以它将不得不执行执行时间types检查,而第一个使用事实,我们已经知道这两个对象是Integer

幸运的是, compareTo知道types,所以:

 if (x.compareTo(y) < 0) 

应该仍然是有效的。 当然,这是微型优化领域,你应该使用你find的最清晰的代码 – 确保它是正确的后:)

正如你所说的,对于包装types( IntegerLong等)和数值types( intlong等)之间的任何比较,包装types的值被取消装箱并且testing被应用于涉及的原始值。

这是作为二进制数字提升的一部分( JLS第5.6.2节 )。 查看每个运营商的文档,看它是否适用。 例如,从==和!=( JLS 15.21.1 )的文档:

如果等于运算符的操作数都是数字types的,或者一个是数字types,另一个是可转换的(§5.1.8)到数字types,则在操作数(§5.6.2)上执行二进制数字提升。

对于<,<=,>和> =( JLS 15.20.1 )

数值比较运算符的每个操作数的types必须是可以转换(§5.1.8)为基本数字types的types,否则会发生编译时错误。 对操作数进行二进制数字提升(第5.6.2节)。 如果提升types的操作数是int或long,则执行有符号整数比较; 如果这个升级types是float或者double,那么执行浮点比较。

请注意,这些都不被认为是两种types都不是数字types的情况的一部分。

==仍然会testing对象的相等性。 然而,被愚弄很容易:

 Integer a = 10; Integer b = 10; System.out.println(a == b); //prints true Integer c = new Integer(10); Integer d = new Integer(10); System.out.println(c == d); //prints false 

你的不平等的例子将起作用,因为它们没有在对象上定义。 但是,通过==比较,对象相等性仍然会被检查。 在这种情况下,当您从盒装原始对象初始化时,将使用相同的对象(对于a和b)。 这是一个好的优化,因为原始框类是不可变的。

==检查引用是否相等,但是在编写如下代码时:

 Integer a = 1; Integer b = 1; 

Java是足够聪明的重复使用相同的不可变ab ,所以这是真的: a == b 。 好奇的是,我写了一个小例子来显示Java停止优化的方式:

 public class BoxingLol { public static void main(String[] args) { for (int i = 0; i < Integer.MAX_VALUE; i++) { Integer a = i; Integer b = i; if (a != b) { System.out.println("Done: " + i); System.exit(0); } } System.out.println("Done, all values equal"); } } 

当我编译和运行(在我的机器上),我得到:

 Done: 128 

tl; dr我认为在检查值相等时使用一元+来触发其中一个操作数的拆箱,否则只需使用math运算符。 理由如下:

前面已经提到, == Integer的比较是身份比较,通常不是程序员想要的,而目的是做价值比较; 我仍然做了一些关于如何在代码的紧凑性,正确性和速度方面进行比较的科学知识。

我使用了一堆通常的方法:

 public boolean method1() { Integer i1 = 7, i2 = 5; return i1.equals( i2 ); } public boolean method2() { Integer i1 = 7, i2 = 5; return i1.intValue() == i2.intValue(); } public boolean method3() { Integer i1 = 7, i2 = 5; return i1.intValue() == i2; } public boolean method4() { Integer i1 = 7, i2 = 5; return i1 == +i2; } public boolean method5() { // obviously not what we want.. Integer i1 = 7, i2 = 5; return i1 == i2; } 

并在编译和反编译后得到这个代码:

 public boolean method1() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); return var1.equals( var2 ); } public boolean method2() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2.intValue() == var1.intValue() ) { return true; } else { return false; } } public boolean method3() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2.intValue() == var1.intValue() ) { return true; } else { return false; } } public boolean method4() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2.intValue() == var1.intValue() ) { return true; } else { return false; } } public boolean method5() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2 == var1 ) { return true; } else { return false; } } 

正如你可以很容易地看到的,方法1调用Integer.equals() (显然),方法2-4得到完全相同的代码 ,通过.intValue()方法展开值,然后直接比较它们,方法5只是触发身份比较,是比较值的不正确方法。

由于(如JS已经提到的) equals()产生开销(它必须执行instanceof和unchecked强制转换),方法2-4将以完全相同的速度工作,当在紧密循环中使用时,注意比方法1更好,因为HotSpot不太可能优化casts和instanceof

它与其他比较运算符(例如< / > )非常相似 – 它们将触发拆箱操作,而使用compareTo()不会 – 但是这次操作可以通过HS高度优化,因为intValue()只是一个getter方法候选人被优化出来)。

在我看来,很less使用的版本4是最简洁的方式 – 每个经验丰富的C / Java开发人员都知道,在大多数情况下,unary plus等同于转换为int / .intValue() – 而对于某些(主要是那些在一生中没有使用一元加号的人),它可以最清楚地表明最简单的意图 – 它表明我们想要一个操作数的int值,强制另一个值也是unbox。 这也是无可辩驳的最类似于用于原始int值的常规i1 == i2比较。

我的投票是针对Integer对象的i1 == +i2i1 > i2样式的,因为性能和一致性的原因。 它还使得代码可以移植到基元,而不需要改变types声明以外的任何东西。 使用命名方法好像给我引入了语义噪声,类似于备受批评的bigInt.add(10).multiply(-3)风格。

调用

 if (a == b) 

大部分时间都可以工作,但不能保证总能正常工作,所以不要使用它。

假设他们被命名为“a”和“b”,比较两个相等的Integer类最正确的方法是调用:

 if(a != null && a.equals(b)) { System.out.println("They are equal"); } 

您也可以使用这种稍微快一点的方式。

  if(a != null && b != null && (a.intValue() == b.intValue())) { System.out.println("They are equal"); } 

在我的机器上,990亿次操作使用第一种方法耗时47秒,第二种方法耗费46秒。 你需要比较数十亿的价值,看看有什么不同。

请注意,“a”可能为空,因为它是一个对象。 以这种方式比较不会导致空指针exception。

为了比较大于和小于,使用

 if (a != null && b!=null) { int compareValue = a.compareTo(b); if (compareValue > 0) { System.out.println("a is greater than b"); } else if (compareValue < 0) { System.out.println("b is greater than a"); } else { System.out.println("a and b are equal"); } } else { System.out.println("a or b is null, cannot compare"); } 

此方法比较两个整数与空检查,看testing

 public static boolean compare(Integer int1, Integer int2) { if(int1!=null) { return int1.equals(int2); } else { return int2==null; } //inline version: //return (int1!=null) ? int1.equals(int2) : int2==null; } //results: System.out.println(compare(1,1)); //true System.out.println(compare(0,1)); //false System.out.println(compare(1,0)); //false System.out.println(compare(null,0)); //false System.out.println(compare(0,null)); //false System.out.println(compare(null,null)); //true