자바 : 정수와 ==


152

자바 1.5로, 당신은 꽤 많이 교류 할 수 Integerint많은 상황에 있습니다.

그러나 코드에서 잠재적 인 결함이 발견되어 조금 놀랐습니다.

다음 코드 :

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

어떤 상황에서 결정할 수는 없지만 값이 같을 때 불일치가 잘못 설정 된 것처럼 보입니다. Eclipse에서 중단 점을 설정하고 Integer값이 모두 137 임을 보았고 부울 표현식을 검사하여 그것이 거짓이라고 말했지만 단계를 넘어 서면 불일치를 true로 설정했습니다.

조건부 변경 :

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

문제를 해결했습니다.

왜 이런 일이 일어 났는지 누군가가 밝힐 수 있습니까? 지금까지 내 PC의 로컬 호스트에서만 동작을 보았습니다. 이 특정한 경우에, 코드는 약 20 번의 비교를 성공적으로 통과했지만 2에서 실패했습니다. 문제는 일관되게 재현 가능합니다.

널리 퍼져있는 문제라면 다른 환경 (dev 및 test)에서 오류가 발생했을 것입니다. 그러나 지금까지이 코드 스 니펫을 실행하는 수백 번의 테스트 후에 아무도 문제를보고하지 않았습니다.

==Integer값 을 비교 하는 데 여전히 합법적이지 않습니까?

아래의 모든 훌륭한 답변 외에도 다음 스택 오버플로 링크에는 약간의 추가 정보가 있습니다. 실제로 내 원래의 질문에 대답했지만 내 질문에 오토 박스를 언급하지 않았기 때문에 선택한 제안에 표시되지 않았습니다.

컴파일러 / JVM이 오토 박싱을“그냥 작동”할 수없는 이유는 무엇입니까?

답변:


238

JVM이 정수 값을 캐싱 중입니다. ==는 -128에서 127 사이의 숫자에서만 작동합니다. http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching


1
감사합니다. 137이 실패한 이유가 확실합니다! 그리고 그것은 왜 그것이 우세한 문제가 아닌지에 대한 나의 질문에 대답합니다. 제가 겪을 사건의 95 %에서 그 가치는 127 미만입니다. 5 %는 아니지만 지금은 이것을 포착하는 것이 좋습니다.
Jeremy Goodell

1
흥미로운 참고 사항 : 몇 주 전까지 cdiCt와 cdsCt는 모두 정수이므로 괜찮 았습니다. 그러나 다르게 처리되는 null 상황을 확인하기 위해 정수로 만들어야했습니다 ...
Jeremy Goodell

3
@Jeremy 그래, 꽤 모호한 문제이지만 일반적으로 Objects에는 .equals ()를 사용하고 primitives에는 ==를 사용합니다. 평등 테스트에는 자동 박스 해제를 사용할 수 없습니다.
Adam

1
롤, 확인 표시를 해주세요! 콜린은 이미 어쨌든 충분한 포인트를 가지고있는 것 같습니다.
Jeremy Goodell

2
new Integer (1)! = new Integer (1)도 참고하십시오. 항상 새 주소를 반환합니다. 오토 박싱은 캐시 된 버전을 사용합니다. 정수를 반환하지 않고 다른 방법으로도 값을 반환 할 수도 있습니다.
Bill K

77

당신은 Integer간단한 두 가지 를 비교할 수 없습니다== 객체를 객체 없으므로 대부분의 시간 참조는 동일하지 않습니다.

Integer-128과 127 사이 의 트릭 이 있습니다. 참조는 Integer.valueOf()작은 정수를 캐시하는 자동 상자 사용과 동일 합니다.

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


자료 :

같은 주제에서 :


1
JLS 또는 Oracle JVM에 대한 보증입니까?
Thorbjørn Ravn Andersen

인용 부분은 JLS에서 온 것이므로 JLS로부터 보증합니다
Colin Hebert

재 : 보증. 나는 아직도 그것에 너무 의존하지 않을 것입니다. new Integer(1) == new Integer(1)여전히 거짓입니다.
Thilo

@Thilo new ... == new ...는 항상 false입니다.
MC 황제

2
@Thilo 사실, equals()객체를 다룰 때 항상 사용하십시오 . 이것은 Java를 배울 때 알아야 할 첫 번째 것 중 하나 여야합니다. 그건 그렇고, 나는의 생성자 Integer가 개인, 즉 인스턴스가 항상 valueOf()메소드를 통해 작성되었다고 추측했을 것 입니다. 그러나 생성자가 공개임을 알았습니다.
MC 황제

5

문제는 두 정수 객체가 바로 그 객체라는 것입니다. 값이 아닌 두 개의 객체 참조를 비교하기 때문에 일치하지 않습니다. .equals객체 참조 비교와 반대로 값 비교를 제공하기 위해 분명히 재정의됩니다.


좋은 답변이지만 왜 137 번만 실패하는지 설명하지는 않습니다.
Jeremy Goodell

4

Integer참조를 참조합니다. 즉, 참조를 비교할 때 값이 아닌 동일한 객체를 가리키는 경우 비교합니다. 따라서 현재보고있는 문제입니다. 일반 int유형 과 잘 작동하는 이유 는에 포함 된 값을 개봉하기 때문 Integer입니다.

당신이하고있는 일을하고 있다면 왜 if진술을 시작 해야한다고 덧붙이 겠습니까?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );

4

"=="는 항상 메모리 위치 또는 값의 객체 참조를 비교합니다. equals 메소드는 항상 값을 비교합니다. 그러나 equals는 간접적으로 "=="연산자를 사용하여 값을 비교합니다.

정수는 정수 캐시를 사용하여 -128에서 +127 사이의 값을 저장합니다. == 연산자를 사용하여 -128에서 127 사이의 값을 확인하면 true를 반환합니다. 이러한 값 이외의 경우 false를 반환합니다.

추가 정보 는 링크 를 참조하십시오


1

사용의 정확성을 위해 다음과 같이 비교 하기 전에 ==비교 된 Integer값 중 하나를 개봉하면 됩니다 ==.

if ( firstInteger.intValue() == secondInteger ) {..

두 번째는 자동으로 개봉됩니다 (물론 null먼저 s 를 확인해야 함 ).


0

이 큰 대답 외에도 내가 배운 것은 다음과 같습니다.

참조로 객체를 비교하지 않는 한 ==와 객체를 비교하지 마십시오.

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