【问题标题】:How can I properly compare two Integers in Java?如何正确比较 Java 中的两个整数?
【发布时间】:2010-12-03 15:34:09
【问题描述】:

我知道,如果您将装箱的原始整数与常量进行比较,例如:

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)

?

【问题讨论】:

  • 好吧,当你尝试的时候发生了什么?你观察到了什么?
  • @Bart Kiers:明确的实验只能反驳,不能证明拆箱的发生。如果使用 == 而不是 equals 会产生正确的结果,那可能是因为装箱的数字被保留或以其他方式重用(大概是作为编译器优化)。问这个问题的原因是要找出内部正在发生的事情,而不是看起来正在发生的事情。 (至少,这就是我在这里的原因。)
  • 咆哮。到目前为止,Java 最愚蠢的事情是无法覆盖运算符,例如 == 和
  • 我试过 Integer a = 4 Integer b = 4; a==b 返回错误。我不得不使用 if (x.intValue() == y.intValue())

标签: java integer autoboxing


【解决方案1】:

否,Integer、Long 等之间的 == 将检查 引用相等性 - 即

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 section 5.1.7)。它仍然使用引用相等,但引用真正相等的。

如果被装箱的值 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))

正如您所说,对于包装器类型(IntegerLong 等)和数字类型(intlong 等)之间的任何比较,包装器类型值是unboxed 并且测试应用于所涉及的原始值。

这发生在二进制数字提升 (JLS section 5.6.2) 中。查看每个操作员的文档以查看它是否已应用。例如,来自 ==!= (JLS 15.21.1) 的文档:

如果相等的操作数 运算符都是数字类型,或 一个是数字类型,另一个是 可转换(第 5.1.8 节)为数字 类型,二进制数字提升是 对操作数执行 (§5.6.2)。

对于&lt;&lt;=&gt;&gt;= (JLS 15.20.1)

每个操作数的类型 数值比较运算符必须是 一种可转换的类型(§5.1.8) 原始数字类型,或 发生编译时错误。二进制 数字促销是在 操作数(§5.6.2)。如果晋升 操作数的类型是 int 或 long, 那么有符号整数比较是 执行;如果这个提升的类型是 float 或 double,然后是浮点数 进行比较。

请注意,在两种类型都不是数字类型的情况下,这些都不被视为的一部分。

【讨论】:

  • 有什么理由要写x.compareTo(y) &lt; 0而不是x &lt; y
  • @MaxNanasy:不是我能马上想到的。
  • 从 Java 1.6.27+ 开始,Integer 类中的 equals 有一个重载,因此它应该与调用 .intValue() 一样高效。它将值作为原始 int 进行比较。
  • 正如@otterslide 所说,这在 Java 8 中不再是必需的。 Integer 与 Integer 的比较默认是按值。
  • @Axel:添加重载不会改变 == 运算符的行为,不是吗?我现在无法进行测试,但如果情况有所改变,我会感到非常惊讶。
【解决方案2】:

== 仍将测试对象是否相等。但是很容易上当:

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

【讨论】:

  • 我认为这是在测试对象相等性。我得到了一些奇怪的结果。我应该用 .equals() 替换它吗?另外,您认为我应该保持不平等还是以其他方式进行?
  • 自动装箱有一些不明显的边缘情况。我将我的 IDE (Eclipse) 设置为将任何未装箱的东西都涂成红色,这在几次情况下使我免于出现错误。如果您要比较两个整数,请使用 .equals,如果您想明确您的不等式,请显式写入强制转换: if ((int)c
  • 如果您将数字文字更改为200,则两个测试都将打印false
  • ... 在大多数 JVM 实现上,也就是说。根据语言规范,结果可能因实现而异。
  • 我认为将其称为“引用相等”更清楚 - 这样您的意思就很明显了。我通常将“对象相等”理解为“equals 被调用的结果”。
【解决方案3】:

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

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

如果参数彼此相等且为 false,则返回 true 否则。因此,如果两个参数都为 null,则返回 true 如果恰好一个参数为 null,则返回 false。否则, 相等是通过使用第一个的equals方法确定的 论据。

【讨论】:

  • 这会处理空值,因此很简单。谢谢!
  • public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b));//这个句柄 null }
【解决方案4】:

我们应该总是使用 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

【讨论】:

    【解决方案5】:

    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

    它与其他比较运算符非常相似(例如 &lt;/&gt;) - 它们会触发拆箱,而使用 compareTo() 不会 - 但这次,HS 高度优化了该操作,因为 intValue()只是一个 getter 方法(被优化的主要候选者)。

    在我看来,很少使用的版本 4 是最简洁的方式 - 每个经验丰富的 C/Java 开发人员都知道一元加号在大多数情况下等于强制转换为 int/.intValue() - 虽然可能有点WTF 对于某些人(主要是那些在他们的一生中没有使用一元加号的人)来说,它可以说是最清楚和最简洁地显示了意图 - 它表明我们想要一个 int 值之一操作数,也强制其他值取消装箱。毫无疑问,它与用于原始 int 值的常规 i1 == i2 比较最为相似。

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

    【讨论】:

    • 你能解释一下方法 4 中的 + 是什么意思吗?我试图用谷歌搜索它,但我只得到了该符号的正常用法(加法、连接)。
    • @AlexLi 这正是我写的 - unary +(一元加号),参见例如stackoverflow.com/questions/2624410/…
    【解决方案6】:

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

    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 -1 用于挥手; stackoverflow.com/questions/15052216/…stackoverflow.com/questions/20897020/…stackoverflow.com/questions/3131136/integers-caching-in-java等详细解释你提到的事情;最好阅读文档(或 lib 源代码)而不是创建具有高局部性风险的伪测试 - 不仅您完全忘记了缓存的下限(即默认为 -128),而且你有一对一(最大值是 127,而不是 128),
    • 但是您完全不能保证在任何机器上都能收到相同的结果 - 因为您可以轻松地自己增加缓存大小,YMMV。此外,OP 的问题是如何正确比较两个整数 - 你根本没有回答
    • 我在这里尊重您的意见和看法。我认为我们只是对 CS 有根本不同的方法。
    • 这不是关于意见 也不是感知 - 它是关于你真诚地错过的事实。做一个伪测试证明什么,没有任何硬支持数据(文档,源等)并且没有回答 OP 的问题,不应该被称为既不是好的问答也不是 CS。至于“不同的方法”——根据定义,CS 是一门科学;你所做的科学不是;这是一个误导性的琐事(或者如果表述得当,它会是一个有趣的评论)——如果你希望它是科学,请更正基本原理您的答案中的缺陷揭穿它们明智地,因为这就是同行评审的工作原理。
    • 当然,我会尝试解决这些缺陷。我没有忘记下限,我觉得它不有趣,所以选择不包括它。我不相信我有一个错误,我说明了java(我在我的机器上澄清,在我的情况下)停止优化它的方式,它是128。如果我已经说明了它这样做的最大值,而不是你是对的答案应该是 127。
    【解决方案7】:

    打电话

    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) 仅适用于较小的值,并且在大多数情况下不起作用。
    • 它可以工作到 127,因为这是 Java 的默认整数缓存,它确保所有到 127 的数字都具有相同的参考值。如果您愿意,可以将缓存设置为高于 127,但为了安全起见,请不要使用 ==。
    • 是的,“大部分时间都可以工作”非常慷慨……你真的应该改写一下
    【解决方案8】:

    在我的例子中,我必须比较两个 Integers 是否相等,其中两个都可能是 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);
    }
    

    【讨论】:

      【解决方案9】:

      因为必须使用正确的运算符基于 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]);
          }
      }
      

      【讨论】:

        【解决方案10】:

        此方法将两个整数与空检查进行比较。查看测试。

        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) 方法而不是自己滚动。
        猜你喜欢
        • 1970-01-01
        • 2013-01-31
        • 1970-01-01
        • 1970-01-01
        • 2018-12-10
        • 1970-01-01
        • 2012-06-05
        • 1970-01-01
        相关资源
        最近更新 更多