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

196 java autoboxing integer

我知道,如果你将盒装原始Integer与常量进行比较,例如:

Integer a = 4;
if (a < 5)
Run Code Online (Sandbox Code Playgroud)

a 将自动取消装箱,比较将起作用.

但是,当您比较两个盒装Integers并希望比较相等或小于/大于?时会发生什么?

Integer a = 4;
Integer b = 5;

if (a == b)
Run Code Online (Sandbox Code Playgroud)

以上代码是否会导致检查它们是否是同一个对象,还是会在这种情况下自动取消装箱?

关于什么:

Integer a = 4;
Integer b = 5;

if (a < b)
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 278

不,= = Integer,Long等将检查引用相等 - 即

Integer x = ...;
Integer y = ...;

System.out.println(x == y);
Run Code Online (Sandbox Code Playgroud)

这将检查是否xy引用相同的对象而不是相同的对象.

所以

Integer x = new Integer(10);
Integer y = new Integer(10);

System.out.println(x == y);
Run Code Online (Sandbox Code Playgroud)

保证打印false."小"自动装箱值的实习可能会导致棘手的结果:

Integer x = 10;
Integer y = 10;

System.out.println(x == y);
Run Code Online (Sandbox Code Playgroud)

true由于拳击规则(JLS第5.1.7节),这将打印.它仍然是使用的引用相等,但引用真的相同的.

就个人而言我会用:

if (x.intValue() == y.intValue())
Run Code Online (Sandbox Code Playgroud)

要么

if (x.equals(y))
Run Code Online (Sandbox Code Playgroud)

后者效率稍差 - 没有重载Integer因此它必须执行执行时类型检查,而第一个使用的事实是我们已经知道两个对象都是Longs.

幸运的是,int了解类型,所以:

Integer x = ...;
Integer y = ...;

System.out.println(x == y);
Run Code Online (Sandbox Code Playgroud)

应该仍然有效率.当然,这是微优化领域,你应该使用你发现最清楚的代码 - 确保它是正确的:)

如你说,对于一个包装型(之间的任何比较long,==等)和数字类型(!=,<等)的包装类型值是装箱和测试被施加到所涉及的原始值.

这是作为二进制数字提升的一部分发生的(JLS第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,则执行浮点比较.

请注意,如果这两种类型都不是数字类型,则不会将其视为一部分.

  • 有什么理由要写`x.compareTo(y) &lt; 0` 而不是`x &lt; y`? (4认同)
  • @MaxNanasy:不是我能立即想到的。 (2认同)
  • 从Java 1.6.27+开始,Integer类中的equals重载,因此它应该与调用.intValue()一样高效.它将值比较为原始int. (2认同)
  • @Axel:不过,添加重载不会改变 == 运算符的行为,是吗?我现在无法进行测试,但如果情况有所改变,我会感到非常惊讶。 (2认同)

Ada*_*wis 41

==仍将测试对象相等性.然而,很容易被愚弄:

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
Run Code Online (Sandbox Code Playgroud)

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

  • 如果将数字文字更改为"200",则两个测试都将打印"false". (11认同)
  • 我认为将这种"参考平等"称为更清晰 - 这就是你的意思.我通常理解"对象平等"是指"调用`equals`的结果". (4认同)
  • ...在大多数JVM实现中。根据语言规范,结果可能在不同的实现之间有所不同。 (2认同)

Jus*_*tas 19

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

java.util.Objects.equals(oneInteger, anotherInteger);
Run Code Online (Sandbox Code Playgroud)

如果参数彼此相等则返回true,否则返回false.因此,如果两个参数都为null,则返回true,如果只有一个参数为null,则返回false.否则,通过使用第一个参数的equals方法确定相等性.

  • 这可以处理空值,因此变得简单。谢谢! (2认同)

Siy*_*lav 14

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

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

请看例子:

情况1:

整数 a = 100; 整数 b = 100;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}
Run Code Online (Sandbox Code Playgroud)

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

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

整数 a = 220; 整数 b = 220;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}
Run Code Online (Sandbox Code Playgroud)

合适的方式:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助。


Cor*_*all 10

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

Integer a = 1;
Integer b = 1;
Run Code Online (Sandbox Code Playgroud)

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Done: 128
Run Code Online (Sandbox Code Playgroud)

  • tl;dr -1 表示挥手;http://stackoverflow.com/questions/15052216/how-large-is-the-integer-cache http://stackoverflow.com/questions/20897020/why-integer-class-caching-values-in-the-range -128-to-127 http://stackoverflow.com/questions/3131136/integers-caching-in-java等详细解释你提到的问题;最好阅读文档(或 lib 源代码),而不是创建具有结果局部性高风险的伪测试 - 不仅您完全忘记了缓存的下限(即默认情况下 -128),而且不仅你有差一(最大值是 127,而不是 128), (4认同)
  • 这与*意见*或*看法*无关——而是与您真诚地错过的*事实*有关。做一个伪测试什么也证明不了,没有任何硬性支持数据(文档、来源等),也没有回答OP的问题,不值得被称为好的问答或CS。至于“不同的方法”——根据定义,CS 是一门“科学”;你做了什么*科学不是*;这是一个*误导性的琐事*(或者如果表述正确的话,这将是一个*有趣的评论*) - 如果您希望它是*科学*,请纠正您答案中的基本缺陷*或*明智地*揭穿它们,因为那就是*同行评审*如何运作。 (2认同)

ott*_*ide 8

调用

if (a == b)
Run Code Online (Sandbox Code Playgroud)

大部分时间都会工作,但不能保证始终有效,所以不要使用它.

比较两个Integer类的相同性的最正确的方法,假设它们被命名为'a'和'b'是调用:

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

您也可以使用稍快一点的方式.

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

在我的机器上,99亿次操作使用第一种方法需要47秒,使用第二种方法需要46秒.您需要比较数十亿的值才能看到任何差异.

请注意,'a'可能为null,因为它是一个Object.以这种方式比较不会导致空指针异常.

比较大于和小于,使用

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");
}
Run Code Online (Sandbox Code Playgroud)


vax*_*uis 7

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;
}
Run Code Online (Sandbox Code Playgroud)

并在编译和反编译后获得此代码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你可以很容易看到的那样,方法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操作数的值,同时强制另一个值为unbox.无可争议地,它i1 == i2与用于原始int值的常规比较最相似.

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