자바 언어·플랫폼 용어 사전
컬렉션

가변 키

해시 컬렉션에 넣은 뒤 equals·hashCode에 쓰이는 필드가 바뀌는 키. 원소가 컬렉션 안에 남은 채 검색·삭제가 안 되는 상태가 된다.

해시 컬렉션은 넣는 순간 계산한 해시로 자리(버킷)를 정하고, 그 뒤로는 다시 계산하지 않는다.

무슨 일이 벌어지나

Set<Member> set = new HashSet<>();   // equals·hashCode 가 name 기반
Member m = new Member("철수");
set.add(m);                          // hash("철수") → 3번 버킷에 저장
m.name = "영희";                     // 필드만 바뀐다. 저장 위치는 그대로

set.contains(m);   // hash("영희") → 9번 버킷을 뒤진다 → false
set.remove(m);     // 같은 이유로 실패
set.size();        // 1 — 못 꺼내고 못 지우는 원소가 남는다

왜 자동으로 재배치되지 않나

컬렉션이 원소의 필드 변경을 알 방법이 없다. 알려면 모든 필드 변경을 감시해야 하는데, 그 비용은 해시 자료구조가 O(1)인 이유 자체를 무너뜨린다. 그래서 자바는 감시 대신 계약을 택했다 — "키는 변하지 않는다."

메모리 관점

버킷 배열의 인덱스는 (n-1) & hash 로 계산된다. 해시가 달라지면 다른 인덱스를 보게 되고, 원소는 여전히 옛 인덱스의 연결 리스트(또는 트리)에 매달려 있다. 탐색은 그 리스트를 아예 방문하지 않으므로 equals 를 비교할 기회조차 없다.

이렇게 피한다

  • 키는 recordfinal 필드로 불변으로 만든다
  • 가변 객체가 불가피하면 식별자(ID)를 키로 쓰고 객체는 값으로 둔다
  • 꼭 바꿔야 한다면 remove → 수정 → add 순서로 꺼냈다 다시 넣는다

면접에서

"HashSet 에 넣은 객체의 필드를 바꾸면 어떻게 되나요"는 자주 나온다. "예외가 나지 않고 조용히 못 찾게 된다" 는 점, 그리고 "반복자로는 여전히 보인다" 는 점까지 말하면 실제로 겪어 본 사람으로 들린다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 컬렉션·예외·리소스