Java에서 두 정수를 올바르게 비교하는 방법은 무엇입니까?


217

박스형 기본 정수를 다음과 같은 상수와 비교하면 다음과 같습니다.

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)

?


16
글쎄, 당신이 시도했을 때 무슨 일이 있었습니까? 무엇을 관찰 했습니까?
Bart Kiers

31
@Bart Kiers : 명백한 실험은 단지 개봉이 일어난다는 것을 증명하지는 못하고 반증 할 수있었습니다. 사용하는 경우 ==대신 equals박스형 번호가 (아마도, 컴파일러 최적화 됨) 구금되거나 재사용되기 때문에 수율 올바른 결과, 그 수있다. 이 질문을하는 이유는 내부에서 일어나는 일이 아닌 어떤 일이 일어나는지 알아 내기 위해서입니다. (적어도, 그것이 내가 여기있는 이유입니다.)
Jim Pivarski

귀하의 계정은 어떻게 되었습니까?

답변:


303

정수, Long 사이의 아니오, ==는 참조 평등 을 확인합니다. 즉

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

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

이 있는지 확인한다 x그리고 y받는 참조 같은 객체 보다는 동일한 개체.

그래서

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가 -128에서 127 사이의 정수형 리터럴 (3.10.1)이거나 부울 리터럴 true 또는 false (§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등) 랩퍼 타입 값은 박스 없음 시험이 관련 프리미티브 값에 적용된다.

이진 숫자 승격의 일부로 발생합니다 ( 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 인 경우 부동 소수점 비교가 수행됩니다.

이 중 어느 것도 어떤 유형도 숫자 유형이 아닌 상황의 일부로 간주 되지 않습니다 .


2
x.compareTo(y) < 0대신에 쓰고 싶은 이유 가 x < y있습니까?
맥스 나나시

1
@ MaxNanasy : 내가 생각할 수있는 것은 아닙니다.
Jon Skeet

2
Java 1.6.27 이상에서 Integer 클래스에는 equals에 과부하가 있으므로 .intValue ()를 호출하는 것만 큼 효율적이어야합니다. 값을 프리미티브 int로 비교합니다.
otterslide

@otterslide가 말했듯이 Java 8에서는 더 이상 필요하지 않습니다. Integer와 Integer의 비교는 기본적으로 값입니다.
Axel Prieto

1
@ Axel : 과부하를 추가해도 == 연산자의 동작이 변경되지 않습니까? 나는 지금 테스트 할 위치에 있지 않지만 그것이 바뀌면 매우 놀랐습니다.
Jon Skeet

44

==여전히 객체 평등을 테스트합니다. 그러나 속이기 쉽습니다.

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 <(int) d) ...; c.compareTo (d) <0 // === c <d
Adam Lewis

12
숫자 리터럴을로 변경하면 200두 테스트가 모두 인쇄 false됩니다.
Daniel Earwicker

2
... 대부분의 JVM 구현에서. 언어 사양에 따르면 결과는 구현마다 다를 수 있습니다.
Daniel Earwicker

4
이 "참조 평등"이라고 부르는 것이 더 명확하다고 생각합니다. 그렇게하면 의미하는 바가 분명합니다. 나는 보통 "오브젝트 평등"을 이해하여 " equals호출 결과"를 의미합니다 .
Jon Skeet

28

Java 1.7부터 Objects.equals 를 사용할 수 있습니다 .

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

인수가 서로 같으면 true를, 그렇지 않으면 false를 반환합니다. 결과적으로 두 인수가 모두 널이면 true가 리턴되고 정확히 하나의 인수가 널이면 false가 리턴됩니다. 그렇지 않으면, 첫 번째 인수의 equals 메소드를 사용하여 동등성을 판별합니다.


이것은 null을 처리하므로 간단합니다. 감사!
대런 파커

10

== 그러나 다음과 같은 코드를 작성할 때 참조 평등을 확인합니다.

Integer a = 1;
Integer b = 1;

Java는 aand b에 대해 동일한 불변의 것을 재사용 할 수있을만큼 똑똑 합니다 a == b. 호기심, 나는 자바가 이런 식으로 최적화를 멈추는 곳을 보여주는 작은 예제를 썼다.

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

1
핸드 웨이빙을위한 tl; dr -1; stackoverflow.com/questions/15052216/… stackoverflow.com/questions/20897020/… stackoverflow.com/questions/3131136/integers-caching-in-java 등 언급 한 사항에 대해 자세히 설명합니다. 결과의 높은 지역성 위험이있는 의사 테스트를 작성하는 것보다 문서 (또는 lib 소스)를 읽는 것이 좋습니다. 캐시의 하한값 (예 : 기본적으로 -128)에 대해 완전히 잊었을뿐만 아니라 당신은 오프에 의해-하나가 (최대하지, 128 127)

그러나 캐시 크기를 YMMV로 쉽게 늘릴 수 있기 때문에 모든 컴퓨터에서 동일한 결과를 보장 받을 수는 없습니다 . 또한, 영업 이익의 질문이었다 제대로 두 개의 정수를 비교하는 방법 - 당신이 전혀 그것을 대답하지 않았습니다 .

나는 당신의 의견과 인식을 존중합니다. CS와는 근본적으로 다른 접근법이 있다고 생각합니다.
Cory Kendall

1
그것은 의견 이나 인식 에 관한 것이 아니라 사실 에 관한 것입니다. 하드 백업 데이터 (문서, 소스 등)없이 OP의 질문에 대답하지 않고 의사 테스트를 수행하는 것은 좋은 Q & A 또는 CS라고 부를 가치가 없습니다. "다른 접근법"에 관해서-CS는 정의상 과학이다 . 당신이 과학을 한 것은 아닙니다 ; 그것은 오해를 불러 일으키는 사소한 일입니다 (또는 적절하게 언급되면 흥미로운 주석 일 것입니다)- 과학 이되기를 원한다면 답의 근본적인 결함을 수정 하거나 현명하게 그것들을 파기하십시오 .공장.

그렇다면 결함을 해결하려고 노력할 것입니다. 나는 하한을 잊지 않았고, 그것이 흥미 롭다고 생각하지 않았고 그것을 포함시키지 않기로 선택했습니다. 나는 하나의 오류로 인해 오프가 있다고 생각하지 않는다. 나는 (내 상황에서 내 컴퓨터에서 명확히 한) java가 이것을 최적화하는 것을 멈추는 방식을 말했다. 이것은 128입니다. 최대 값을 말하면 이것은 당신이 옳은 것보다 답이 127이었을 것입니다.
Cory Kendall

8

TL은, 박사 내 의견은 단항를 사용하는 것입니다+ 값 평등을 확인할 때 피연산자 중 하나에서 unboxing을 트리거하고 그렇지 않으면 단순히 수학 연산자를 사용하는 것입니다. 근거는 다음과 같습니다.

이미 언급 된 ==에 대한 비교가 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캐스트와 확인되지 않은 캐스트) HotSpot이 아니기 때문에 방법 2-4는 정확히 동일한 속도로 작동합니다. 캐스트를 최적화 할 가능성이 높습니다 instanceof.

다른 비교 연산자 (예 : </ >) 와 매우 유사합니다. 사용 compareTo()하지 않는 동안 언 박싱을 트리거 하지만 이번에는 HS에 의해 작업을 최적화 할 수 있습니다.intValue() 는 getter 방법 (최적화 후보) 할 수 있습니다.

내 의견으로는, 거의 사용되지 않는 버전 4가 가장 간결한 방법입니다. 모든 노련한 C / Java 개발자는 단항 플러스가 대부분의 경우 int/와 동일하다는 것을 알고 .intValue()있습니다. 일부 사람들에게는 약간의 WTF 순간 이 될 수 있습니다. 평생 동안 단항 더하기를 사용하지 않는 것), 가장 분명하고 가장 의도를 의도적으로 보여줍니다- int피연산자 중 하나의 가치를 원한다는 것을 보여줍니다 . 다른 가치도 개봉해야합니다. 또한 i1 == i2기본 int값에 사용되는 정규 비교 와 가장 유사 합니다.

내 투표는 간다 i1 == +i2& i1 > i2에 대한 스타일의 Integer성능 및 일관성을 이유로 모두 객체. 또한 형식 선언 이외의 것을 변경하지 않고도 코드를 프리미티브로 이식 할 수 있습니다. 명명 된 방법을 사용하는 것은 비판적 인 bigInt.add(10).multiply(-3)스타일 과 비슷한 의미 적 노이즈를 도입하는 것처럼 보입니다 .


방법 4에서 +의 의미를 설명 할 수 있습니까? 나는 그것을 시도했지만 그 기호의 일반적인 사용법 (추가, 연결) 만 얻었습니다.
Alex Li

1
@AlexLi 그것은 내가 쓴 것을 의미합니다- unary +(단항 플러스), 예를 들어 stackoverflow.com/questions/2624410/…

8

부름

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'는 Object이므로 null 일 수 있습니다. 이 방법으로 비교해도 널 포인터 예외는 발생하지 않습니다.

보다 크거나 작은 것을 비교하려면

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

1
if (a==b)작은 값에만 작동하며 대부분의 경우 작동하지 않습니다.
Tony

Java의 기본 정수 캐시이므로 최대 127까지 작동하므로 최대 127 개의 모든 숫자가 동일한 참조 값을 갖습니다. 원하는 경우 캐시를 127보다 높게 설정하도록 설정할 수 있지만 안전을 위해 ==를 사용하지 마십시오.
otterslide

3

두 정수를 비교하기 위해 항상 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");
}

위의 경우 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");
}

적절한 방법:

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

이게 도움이 되길 바란다.


1

내 경우에는 두 가지 Integer가 모두 동일한 평등을 위해 두 s 를 비교 해야했습니다 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);
}

-1

비교 방법은 올바른 연산자를 사용하여 유형 int (x == y) 또는 클래스 정수 (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]);
    }
}

-2

이 방법은 두 정수를 null 검사와 비교합니다. 테스트 참조

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

4
이를 Objects.equals(x,y)위해 자신의 롤링 대신 메소드 를 사용 하는 것이 더 낫습니다 .
ryvantage 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.