오토박싱이 부르는 Integer.valueOf 는 작은 값을 새로 만들지 않고 캐시에서 꺼낸다.
Integer a = 127, b = 127;
a == b; // true — 같은 캐시 객체
Integer c = 128, d = 128;
c == d; // false — 새 객체 둘
어디까지가 보장인가
- −128 ~ 127 은 자바 언어 명세(JLS 5.1.7)가 보장한다. 두 번의 박싱 결과가 항상
==다 - 정확히는 상수 표현식을 박싱할 때의 보장이다. 같은 규칙이
boolean,char(\u0000~\u007f),byte·short·long에도 적용된다 - 그 밖은 구현이 캐시해도 되고 안 해도 된다(HotSpot 은
-XX:AutoBoxCacheMax로 상한 조정). 명세는 이 구간에 대해 동일성을 가정하지 말라고 명시한다
두 번째가 더 나쁘다. == 결과가 실행 환경에 따라 달라지므로, 테스트에서는 통과하고
운영에서 실패하는 유형의 버그가 된다.
왜 캐시를 두었나
박싱은 객체 할당이다. 반복문에서 작은 정수를 다룰 때마다 힙에 객체를 만들면 할당과 GC 부담이 커진다. 자주 쓰이는 좁은 구간만 미리 만들어 재사용하는 것이 전형적인 플라이웨이트 절충이다.
컬렉션과의 관계
contains·indexOf·remove(Object) 는 내부에서 equals 를 쓰므로 값 비교가 된다.
문제는 사람이 직접 == 로 비교할 때다.
if (list.get(0) == list.get(1)) // 128 이상이면 값이 같아도 false
if (list.get(0).equals(list.get(1))) // 이렇게 쓴다
if (list.get(0).intValue() == ...) // 또는 원시 타입으로 꺼내 비교
면접에서
"Integer 두 개를 == 로 비교하면?" 에 "127까지는 true, 그 위는 보장되지 않는다" 까지
답하면 외운 게 아니라 이해한 것으로 들린다.