如何在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)

?

592275 次浏览

==仍将测试对象是否相等。然而,这很容易被愚弄:

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)。这是一个不错的优化,因为原语盒类是不可变的。

No, == between Integer, Long etc将检查参考平等 -即。

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);

这将打印true,由于装箱规则(JLS第5.1.7节)。它仍然使用了引用相等,但引用真正的相等。

如果被装箱的值p是一个int类型的整数字面值 -128和127包括(§3.10.1),或布尔字面值真或假(§3.10.3),或'\u0000'和'\u007f'之间的字符字面值 包含(§3.10.4),则设a和b是任意两个装箱的结果 它总是a == b.

我个人会使用:

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

if (x.equals(y))

正如你所说,对于包装器类型(IntegerLong等)和数字类型(intlong等)之间的任何比较,包装器类型值是从箱子中取出,并且测试应用于涉及的原语值。

这是二进制数字提升(JLS第5.6.2节)的一部分。查看每个操作符的文档,看看它是否适用。例如,在==!= (JLS 15.21.1)的文档中:

如果操作数是一个等式 运算符都是数值类型或 一个是数字类型,另一个是数字类型 是否可转换(§5.1.8)为数字 类型,二进制数字提升是 在操作数上执行(§5.6.2)

<<=>>= (JLS 15.20.1)

a的每个操作数的类型 数值比较运算符必须是 可转换(§5.1.8)的类型 基元数字类型 发生编译时错误。二进制 上执行数字提升 操作数(§5.6.2)。如果升职了 操作数的类型是int或long, 那么有符号整数比较是 执行;如果此提升类型为 Float或double,然后是浮点数

.执行比较

注意,在既不类型是数字类型的情况下,这些都不被认为是情况的一部分。

我的意见是在检查值是否相等时使用一元+来触发对其中一个操作数的开箱操作,否则只需使用数学操作符。基本原理:

前面已经提到,==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和未检查的强制转换),方法2-4将以完全相同的速度工作,在紧循环中使用时明显优于方法1,因为HotSpot不太可能优化出强制转换&instanceof

它与其他比较操作符(例如</>)非常相似——它们将触发开箱,而使用compareTo()则不会——但这一次,由于intValue()只是一个getter方法(被优化的主要候选方法),操作由HS高度优化。

在我看来,很少使用版本4是最简洁的方式,每一个经验丰富的C / Java开发人员都知道,在大多数情况下等于一元+演员int / .intValue()——尽管它可能有点WTF时刻一些(主要是那些没有使用加号一生中),它可以说是显示意图最明显和最简洁地——这表明我们想要一个int价值的一个操作数,迫使其他值从箱子中取出。毫无疑问,它也最类似于用于原语int值的常规i1 == i2比较。

我投票给i1 == +i2 &Integer对象的i1 > i2样式,用于性能和amp;一致性的原因。它还使代码可以移植到原语,而不需要更改类型声明以外的任何内容。使用命名方法对我来说似乎是引入语义噪声,类似于备受批评的bigInt.add(10).multiply(-3)风格。

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

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

调用

if (a == b)

大部分时间都有效,但不能保证总是有效,所以不要使用它。

比较两个Integer类是否相等的最合适的方法是,假设它们分别命名为'a'和'b',调用:

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'可能是空的,因为它是一个对象。以这种方式进行比较不会导致空指针异常。

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

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");
}

这个方法用空检查比较两个Integer。请参阅测试。

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

从Java 1.7开始,你可以使用Objects.equals:

java.util.Objects.equals(oneInteger, anotherInteger);

如果参数相等则返回true,如果参数为false则返回false 否则。因此,如果两个参数都为空,则返回true 如果恰好有一个参数为null,则返回false。否则, 等号是用第一个的等号法来确定的 论点。< / p >

在我的情况下,我必须比较两个__abc0是否相等,其中它们都可以是null。我搜索了类似的主题,但没有找到合适的。我想出了一个简单的效用函数:

public static boolean integersEqual(Integer i1, Integer i2) {
if (i1 == null && i2 == null) {
return true;
}
if (i1 == null && i2 != null) {
return false;
}
if (i1 != null && i2 == null) {
return false;
}
return i1.intValue() == i2.intValue();
}


// Considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
if (i1 == null && i2 == null) {
return 0;
}
if (i1 == null && i2 != null) {
return -1;
}
return i1.compareTo(i2);
}

因为比较方法必须基于类型int (x==y)或类Integer (x= (y)),并使用正确的操作符:

public class Example {


public static void main(String[] args) {
int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};


for(int j=1; j<arr.length-1; j++)
if((arr[j-1] != arr[j]) && (arr[j] != arr[j+1]))
System.out.println("int>" + arr[j]);


Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};


for(int j=1; j<I_arr.length-1; j++)
if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1])))
System.out.println("Interger>" + I_arr[j]);
}
}

我们应该总是使用equals()方法来比较两个整数。这是推荐的做法。

如果我们使用==比较两个整数,由于JVM的内部优化,这将适用于一定范围的整数值(从-128到127的整数值)。

请看例子:

案例1:

Integer a = 100;
Integer b = 100;


if (a == b) {
System.out.println("a and b are equal");
} else {
System.out.println("a and b are not equal");
}

在上述情况下,JVM从缓存池中使用a和b的值,并返回相同的对象实例(因此内存地址)的整数对象,我们得到两者相等。它的JVM优化用于某些范围值。

案例2:在这种情况下,a和b不相等,因为它没有从-128到127的范围。

Integer a = 220;
Integer b = 220;
   

if (a == b) {
System.out.println("a and b are equal");
} else {
System.out.println("a and b are not equal");
}

正确的方法:

Integer a = 200;
Integer b = 200;
System.out.println("a == b? " + a.equals(b)); // true