我知道,如果您将盒装原始整数与常量进行比较,例如:
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)
?
==
而不是 equals
产生正确的结果,那可能是因为装箱的数字正在被保留或以其他方式重用(大概是作为编译器优化)。问这个问题的原因是要找出内部正在发生的事情,而不是看起来正在发生的事情。 (至少,这就是我在这里的原因。)
不,== 在 Integer、Long 等之间将检查引用是否相等 - 即
Integer x = ...;
Integer y = ...;
System.out.println(x == y);
这将检查 x
和 y
是否引用 相同的对象 而不是 equal 对象。
所以
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 section 5.1.7),这将打印 true
。它仍然使用引用相等,但引用真正是相等。
如果被装箱的值 p 是 int 类型的整数文字,介于 -128 和 127 之间(第 3.10.1 节),或者布尔文字真或假(第 3.10.3 节),或者是介于 '\u0000' 和'\u007f' (第 3.10.4 节),则令 a 和 b 为 p 的任意两次装箱转换的结果。 a == b 总是如此。
我个人会使用:
if (x.intValue() == y.intValue())
或者
if (x.equals(y))
正如您所说,对于包装类型(Integer
、Long
等)和数字类型(int
、long
等)之间的任何比较,包装类型值是 unboxed 并且test 应用于所涉及的原始值。
这作为二进制数字提升 (JLS section 5.6.2) 的一部分发生。查看每个操作员的文档以查看它是否已应用。例如,来自 ==
和 !=
(JLS 15.21.1) 的文档:
如果相等运算符的操作数都是数字类型,或者一个是数字类型而另一个是可转换的(第 5.1.8 节)为数字类型,则对操作数执行二进制数字提升(第 5.6.2 节)。
对于 <
、<=
、>
和 >=
(JLS 15.20.1)
数值比较运算符的每个操作数的类型必须是可转换(第 5.1.8 节)为原始数值类型的类型,否则会发生编译时错误。对操作数执行二进制数字提升(第 5.6.2 节)。如果操作数的提升类型是 int 或 long,则执行有符号整数比较;如果此提升的类型是 float 或 double,则执行浮点比较。
请注意,这些都不被视为两种类型都不是数字类型的情况的一部分。
==
仍将测试对象相等性。然而,很容易被愚弄:
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)。这是一个不错的优化,因为原始盒子类是不可变的。
200
,则两个测试都将打印 false
。
equals
的结果”。
从 Java 1.7 开始,您可以使用 Objects.equals:
java.util.Objects.equals(oneInteger, anotherInteger);
如果参数彼此相等,则返回 true,否则返回 false。因此,如果两个参数都为 null,则返回 true,如果恰好一个参数为 null,则返回 false。否则,相等性是通过使用第一个参数的 equals 方法来确定的。
我们应该总是使用 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
tl;dr 我的意见是在检查值相等时使用一元 +
来触发其中一个操作数的拆箱,否则只需使用数学运算符。理由如下:
前面已经提到,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()
不会 - 但这次,HS 高度优化了该操作,因为 intValue()
只是一个getter 方法(被优化的主要候选人)。
在我看来,很少使用的版本 4 是最简洁的方式 - 每个经验丰富的 C/Java 开发人员都知道一元加号在大多数情况下等于强制转换为 int
/.intValue()
- 虽然它可能有点 WTF 对于某些人(主要是那些一生中没有使用一元加号的人)来说,它可以说是最清楚、最简洁地显示了意图 - 它表明我们想要一个操作数之一的 int
值,强制其他值也取消装箱。毫无疑问,它与用于原始 int
值的常规 i1 == i2
比较最为相似。
我投票给i1 == +i2
& Integer
个对象的 i1 > i2
样式,用于性能和一致性的原因。它还使代码可移植到原语,而无需更改类型声明以外的任何内容。使用命名方法似乎给我带来了语义噪音,类似于备受批评的 bigInt.add(10).multiply(-3)
风格。
unary +
(一元加号),参见例如 stackoverflow.com/questions/2624410/…
==
检查引用相等性,但是在编写如下代码时:
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
打电话
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”可能为空,因为它是一个对象。这样比较不会导致空指针异常。
为了比较大于和小于,使用
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");
}
if (a==b)
仅适用于较小的值,并且在大多数情况下都不起作用。
在我的情况下,我必须比较两个 Integer
的相等性,它们都可能是 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.equals(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]);
}
}
此方法将两个整数与空检查进行比较。查看测试。
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
Objects.equals(x,y)
方法而不是自己滚动。
x.compareTo(y) < 0
而不是x < y
?