简体   繁体   English

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

[英]How can I properly compare two Integers in Java?

I know that if you compare a boxed primitive Integer with a constant such as:我知道,如果您将盒装原始 Integer 与常量进行比较,例如:

Integer a = 4;
if (a < 5)

a will automatically be unboxed and the comparison will work. a将自动被取消装箱并且比较将起作用。

However, what happens when you are comparing two boxed Integers and want to compare either equality or less than/greater than?但是,当您比较两个装箱Integers并想要比较相等或小于/大于时会发生什么?

Integer a = 4;
Integer b = 5;

if (a == b)

Will the above code result in checking to see if they are the same object, or will it auto-unbox in that case?上面的代码会导致检查它们是否是相同的 object,还是会在这种情况下自动拆箱?

What about:关于什么:

Integer a = 4;
Integer b = 5;

if (a < b)

? ?

No, == between Integer, Long etc will check for reference equality - ie不,Integer、Long 等之间的 == 将检查引用相等性- 即

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

System.out.println(x == y);

this will check whether x and y refer to the same object rather than equal objects.这将检查xy是否引用同一个对象而不是相等的对象。

So所以

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

System.out.println(x == y);

is guaranteed to print false .保证打印false Interning of "small" autoboxed values can lead to tricky results: “小”自动装箱值的实习可能会导致棘手的结果:

Integer x = 10;
Integer y = 10;

System.out.println(x == y);

This will print true , due to the rules of boxing ( JLS section 5.1.7 ).由于拳击规则( JLS 第 5.1.7 节),这将打印true It's still reference equality being used, but the references genuinely are equal.它仍然参考平等使用,但引用真正相等的。

If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\' and '\' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p.如果被装箱的值 p 是介于 -128 和 127 之间的 int 类型的整数文字(第 3.10.1 节),或布尔文字 true 或 false(第 3.10.3 节),或介于 '\' 和'\' 包含(第 3.10.4 节),然后让 a 和 b 是 p 的任何两个装箱转换的结果。 It is always the case that a == b. a == b 总是这样。

Personally I'd use:我个人会使用:

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

or或者

if (x.equals(y))

As you say, for any comparison between a wrapper type ( Integer , Long etc) and a numeric type ( int , long etc) the wrapper type value is unboxed and the test is applied to the primitive values involved.正如您所说,对于包装器类型( IntegerLong等)和数字类型( intlong等)之间的任何比较,包装器类型值都被取消装箱,并且测试应用于所涉及的原始值。

This occurs as part of binary numeric promotion ( JLS section 5.6.2 ).这是作为二进制数字提升的一部分发生的( JLS 第 5.6.2 节)。 Look at each individual operator's documentation to see whether it's applied.查看每个操作员的文档,看看它是否被应用。 For example, from the docs for == and != ( JLS 15.21.1 ):例如,来自==!= ( JLS 15.21.1 ) 的文档:

If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).如果相等运算符的操作数都是数字类型,或者一个是数字类型而另一个可转换(第 5.1.8 节)为数字类型,则对操作数执行二进制数字提升(第 5.6.2 节)。

and for < , <= , > and >= ( JLS 15.20.1 )对于<<=>>= ( JLS 15.20.1 )

The type of each of the operands of a numerical comparison operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.数值比较运算符的每个操作数的类型必须是可转换(第 5.1.8 节)为原始数值类型的类型,否则会发生编译时错误。 Binary numeric promotion is performed on the operands (§5.6.2).对操作数执行二进制数字提升(第 5.6.2 节)。 If the promoted type of the operands is int or long, then signed integer comparison is performed;如果操作数的提升类型是 int 或 long,则进行有符号整数比较; if this promoted type is float or double, then floating-point comparison is performed.如果此提升类型为 float 或 double,则执行浮点比较。

Note how none of this is considered as part of the situation where neither type is a numeric type.请注意,这些都不会被视为两种类型都不是数字类型的情况的一部分。

== will still test object equality. ==仍将测试对象相等性。 It is easy to be fooled, however:然而,很容易被愚弄:

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

Your examples with inequalities will work since they are not defined on Objects.您的不等式示例将起作用,因为它们未在对象上定义。 However, with the == comparison, object equality will still be checked.但是,使用==比较时,仍会检查对象相等性。 In this case, when you initialize the objects from a boxed primitive, the same object is used (for both a and b).在这种情况下,当您从装箱基元初始化对象时,将使用相同的对象(对于 a 和 b)。 This is an okay optimization since the primitive box classes are immutable.这是一个不错的优化,因为原始框类是不可变的。

Since Java 1.7 you can use Objects.equals :从 Java 1.7 开始,您可以使用Objects.equals

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

Returns true if the arguments are equal to each other and false otherwise.如果参数彼此相等,则返回 true,否则返回 false。 Consequently, if both arguments are null, true is returned and if exactly one argument is null, false is returned.因此,如果两个参数都为 null,则返回 true,如果正好有一个参数为 null,则返回 false。 Otherwise, equality is determined by using the equals method of the first argument.否则,通过使用第一个参数的 equals 方法确定相等。

We should always go for the equals() method for comparison of two integers.我们应该总是使用 equals() 方法来比较两个整数。 It's the recommended practice.这是推荐的做法。

If we compare two integers using == that would work for certain range of integer values (Integer from -128 to 127) due to the JVM's internal optimisation.如果我们使用 == 比较两个整数,由于 JVM 的内部优化,这将适用于特定范围的整数值(整数从 -128 到 127)。

Please see examples:请看例子:

Case 1:情况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");
}

In above case JVM uses value of a and b from cached pool and return the same object instance(therefore memory address) of integer object and we get both are equal.Its an optimisation JVM does for certain range values.在上述情况下,JVM 使用缓存池中 a 和 b 的值并返回整数对象的相同对象实例(因此是内存地址),我们得到两者相等。这是JVM针对某些范围值所做的优化

Case 2: In this case, a and b are not equal because it does not come with the range from -128 to 127.情况 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");
}

Proper way:合适的方式:

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

tl;dr my opinion is to use a unary + to trigger the unboxing on one of the operands when checking for value equality, and simply use the maths operators otherwise. tl; dr我的意见是在检查值相等时使用一元+来触发对一个操作数的拆箱,否则只需使用数学运算符。 Rationale follows:原理如下:

It has been mentioned already that == comparison for Integer is identity comparison, which is usually not what a programmer want, and that the aim is to do value comparison;前面已经提到,对于Integer ==比较是恒等比较,这通常不是程序员想要的,目的是进行值比较; still, I've done a little science about how to do that comparison most efficiently, both in term of code compactness, correctness and speed.尽管如此,我还是做了一些关于如何最有效地进行比较的科学,无论是在代码紧凑性、正确性还是速度方面。

I used the usual bunch of methods:我使用了通常的一堆方法:

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

and got this code after compilation and decompilation:并在编译和反编译后得到这段代码:

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

As you can easily see, method 1 calls Integer.equals() (obviously), methods 2-4 result in exactly the same code , unwrapping the values by means of .intValue() and then comparing them directly, and method 5 just triggers an identity comparison, being the incorrect way to compare values.如您所见,方法1调用Integer.equals() (显然),方法2-4产生完全相同的代码,通过.intValue()解包值然后直接比较它们,方法5只是触发身份比较,这是比较值的错误方式。

Since (as already mentioned by eg JS) equals() incurs an overhead (it has to do instanceof and an unchecked cast), methods 2-4 will work with exactly the same speed, noticingly better than method 1 when used in tight loops, since HotSpot is not likely to optimize out the casts & instanceof .由于(如 JS 已经提到的) equals()会产生开销(它必须执行instanceof和未经检查的强制转换),因此方法 2-4 将以完全相同的速度工作,在紧密循环中使用时明显优于方法 1,因为 HotSpot 不太可能优化 casts & instanceof

It's quite similar with other comparison operators (eg < / > ) - they will trigger unboxing, while using compareTo() won't - but this time, the operation is highly optimizable by HS since intValue() is just a getter method (prime candidate to being optimized out).它与其他比较运算符(例如< / > )非常相似——它们会触发拆箱,而使用compareTo()不会——但这一次,HS 高度优化了操作,因为intValue()只是一个 getter 方法(主要被优化的候选者)。

In my opinion, the seldom used version 4 is the most concise way - every seasoned C/Java developer knows that unary plus is in most cases equal to cast to int / .intValue() - while it may be a little WTF moment for some (mostly those who didn't use unary plus in their lifetime), it arguably shows the intent most clearly and most tersely - it shows that we want an int value of one of the operands, forcing the other value to unbox as well.在我看来,很少使用的第 4 版是最简洁的方式——每个经验丰富的 C/Java 开发人员都知道一元加在大多数情况下等于.intValue()int / .intValue() ——而对于某些人来说,这可能是一个小小的WTF时刻(主要是那些一生中没有使用过一元加号的人),它可以说是最清晰、最简洁地展示了意图——它表明我们想要一个操作数的int值,迫使另一个值也取消装箱。 It is also unarguably most similar to the regular i1 == i2 comparison used for primitive int values.它也无疑与用于原始int值的常规i1 == i2比较最相似。

My vote goes for i1 == +i2 & i1 > i2 style for Integer objects, both for performance & consistency reasons.出于性能和一致性的原因,我对Integer对象的投票支持i1 == +i2 & i1 > i2样式。 It also makes the code portable to primitives without changing anything other than the type declaration.它还使代码可移植到原语,而无需更改除类型声明之外的任何内容。 Using named methods seems like introducing semantic noise to me, similar to the much-criticized bigInt.add(10).multiply(-3) style.使用命名方法似乎给我带来了语义噪音,类似于备受批评的bigInt.add(10).multiply(-3)风格。

== checks for reference equality, however when writing code like: ==检查引用相等性,但是在编写如下代码时:

Integer a = 1;
Integer b = 1;

Java is smart enough to reuse the same immutable for a and b , so this is true: a == b . Java 足够聪明,可以为ab重用相同的不可变参数,所以这是真的: a == b Curious, I wrote a small example to show where java stops optimizing in this way:好奇,我写了一个小例子来说明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");
    }
}

When I compile and run this (on my machine), I get:当我编译并运行它(在我的机器上)时,我得到:

Done: 128

Calling打电话

if (a == b)

Will work most of the time, but it's not guaranteed to always work, so do not use it.大部分时间都会工作,但不能保证总是工作,所以不要使用它。

The most proper way to compare two Integer classes for equality, assuming they are named 'a' and 'b' is to call:比较两个 Integer 类是否相等的最正确方法,假设它们被命名为 'a' 和 'b' 是调用:

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

You can also use this way which is slightly faster.您也可以使用这种稍微快一点的方式。

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

On my machine 99 billion operations took 47 seconds using the first method, and 46 seconds using the second method.在我的机器上,990 亿次操作使用第一种方法需要 47 秒,使用第二种方法需要 46 秒。 You would need to be comparing billions of values to see any difference.您需要比较数十亿个值才能看到任何差异。

Note that 'a' may be null since it's an Object.请注意,'a' 可能为空,因为它是一个对象。 Comparing in this way will not cause a null pointer exception.这样比较不会导致空指针异常。

For comparing greater and less than, use要比较大于和小于,请使用

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

In my case I had to compare two Integer s for equality where both of them could be null .在我的情况下,我必须比较两个Integer的相等性,其中它们都可以是null I searched similar topics, but I didn't find anything elegant for this.我搜索了类似的主题,但我没有找到任何优雅的东西。 I came up with simple utility function:我想出了简单的效用函数:

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

Because a comparison method has to be done based on type int (x==y) or class Integer (x.equals(y)) with the right operator:因为必须基于类型 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]);
    }
}

This method compares two Integer's with a null check.此方法将两个整数与空检查进行比较。 See the tests.请参阅测试。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM