如何正确比较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);
这将检查x
和y
是否指向相同的对象而不是相等的对象。
所以
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( Integer
, Long
等)和数值types( int
, long
等)之间的任何比较,包装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是足够聪明的重复使用相同的不可变a
和b
,所以这是真的: 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 == +i2
& i1 > 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