Java에서 정수 랩퍼를 비교할 때 128 == 128은 거짓이지만 127 == 127은 왜됩니까?


172
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

산출:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

산출:

true

참고 : -128과 127 사이의 숫자는 true입니다.



1
이 질문을 어떻게 할 수 있었습니까? 정말 재밌지 만, "실제 세계에서"그런 식으로 오지 마라.
Mare Infinitus 2016 년

답변:


217

Java에서 숫자 리터럴을 컴파일하고이를 정수 (capital I)에 지정하면 컴파일러에서 다음을 생성합니다.

Integer b2 =Integer.valueOf(127)

이 코드 줄은 오토 박싱을 사용할 때도 생성됩니다.

valueOf 특정 숫자가 "풀링"되도록 구현되고 128보다 작은 값에 대해 동일한 인스턴스를 반환합니다.

Java 1.6 소스 코드에서 621 행 :

public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}

high캔 시스템 특성과 다른 값으로 설정 될 수있다.

-Djava.lang.Integer.IntegerCache.high = 999

해당 시스템 속성으로 프로그램을 실행하면 true로 출력됩니다!

명백한 결론 : 두 개의 참조가 동일하다고 절대 의존하지 말고 항상 .equals()메소드 와 비교하십시오 .

따라서 b2.equals(b3)논리적으로 동일한 모든 b2, b3 값에 대해 true를 인쇄합니다.

Integer캐시는 성능상의 이유가 아니라 JLS 섹션 5.1.7 을 준수 한다는 점에 유의하십시오 . -128에서 127 사이의 값에 대해 개체 ID를 제공해야합니다.

Integer # valueOf (int) 도이 동작을 문서화합니다.

이 방법은 자주 요청되는 값을 캐싱하여 공간 및 시간 성능을 크게 향상시킬 수 있습니다. 이 방법은 항상 -128에서 127 사이의 값을 캐시하며이 범위 밖의 다른 값을 캐시 할 수 있습니다.


1
127보다 작은 값은 java에 의해 무시되고 Integer.MAX_VALUE-128보다 큰 값은 제한됩니다.
안드레아스 피터슨

정수는 Java 5 이상에서 바이트 값에 대해 캐시되어 new Integer (1) == new Integer (1)가됩니다. 그러나 Java 1.4 이하에서는 그렇지 않으므로 결국 해당 환경으로 다운 그레이드해야하는 경우주의하십시오.
MetroidFan2002

11
아니, 이건 잘못이야 new Integer (1) == new Integer (1)은 jvm에 관계없이 false입니다. AFAIK 컴파일러는 "new"키워드를 속이지 않습니다. 항상 새 객체를 인스턴스화해야합니다.
Andreas Petersson

1
@Holger 재미있는 포인트. 다음 멀리 최적화에 허용되지 않는 부작용이있을 수있다 - 그러나 (누군가가 미친 될 이유를 묻지 않는다) ... 사용자 정의 IMPL으로 JDK에서 정수 클래스를 대체하는 것은 기술적으로 가능하다
안드레아스 피터슨

1
트윗 담아 가기 "컴파일러"는 JIT 컴파일러를 의미하며, 실제 구현 클래스를 정확하게 알고 생성자가 부작용이없는 경우에만 최적화 할 수 있습니다. 또는 부작용 만 재현하고 식을 사용하여 식을 최적화하십시오 false. 실제로, 이스케이프 분석 및 스칼라 교체를 적용한 부작용으로 오늘날에도 이미 발생할 수 있습니다.
Holger

24

오토 박싱은 -128에서 127까지 캐시합니다. 이는 JLS ( 5.1.7 )에 지정되어 있습니다.

박스형 p 가 true, false, 바이트, \ u0000 ~ \ u007f 범위의 문자 또는 -128과 127 사이의 정수 또는 짧은 숫자 인 경우 r1과 r2를 두 개의 권투 변환의 결과로 둡니다. p. 항상 r1 == r2입니다.

객체를 다룰 때 기억해야 할 간단한 규칙 .equals은 두 객체가 "동일한" ==것인지 확인하려는 경우 동일한 인스턴스를 가리키는 지 확인하려는 경우에 사용하는 것입니다.


1
참고 : JLS는 Java 9에서 변경되었습니다. 이제 컴파일 시간 상수 표현식에 대해서만 보장됩니다 . 허용 된 답변으로 업데이트를 참조하십시오.
Stephen C

9

기본 데이터 유형 인 int를 사용하면 두 경우 모두 예상 출력이 true가됩니다.

그러나 Integer 객체를 사용하고 있기 때문에 == 연산자는 다른 의미를 갖습니다.

객체의 맥락에서 ==는 변수가 동일한 객체 참조를 참조하는지 확인합니다.

객체의 값을 비교하려면 equals () 메소드를 사용해야합니다

 b2.equals(b1)

b2가 b1보다 작거나 크거나 같은지 여부를 나타냅니다 (자세한 내용은 API 확인).


7

Java 관련 메모리 최적화입니다.

메모리를 절약하기 위해 Java는 값이 다음 범위에 속하는 모든 랩퍼 오브젝트를 '재사용'합니다.

모든 부울 값 (true 및 false)

모든 바이트 값

\ u0000에서 \ u007f까지의 모든 문자 값 (예 : 10 진수로 0에서 127)

-128에서 127 사이의 모든 Short 및 Integer 값


3

값이 -128 ~ 127 사이 인 경우 Integer.java에서보세요, 그렇게, 캐시 풀을 사용 (Integer) 1 == (Integer) 1하는 동안(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}       

0

다른 답변은 관찰 된 효과가 관찰 될 수있는 이유를 설명하지만, 실제로 프로그래머에게는 중요하지 않습니다 (물론 흥미롭지 만 실제 코드를 작성할 때 잊어 버릴 수있는 것).

Integer 객체가 동일한 지 비교하려면이 equals방법을 사용하십시오 .

ID 연산자를 사용하여 Integer 객체가 동일한 지 비교하지 마십시오 ==.

동일한 값이 동일한 객체 일 수도 있지만 일반적으로 의존해야하는 것은 아닙니다.


-4

이 문제는 Integer에만 국한되지 않으므로 다음을 작성했습니다. 내 결론은 API를 잘못 사용하면 틀린 행동을 보게 될 것입니다. 올바르게 사용하면 올바른 동작이 나타납니다.

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.