
equals를 재정의하면 같은 논리적 값을 가진 객체들은 서로 동일한 hashCode를 반환해야 한다.hashCode를 재정의하지 않으면 않으면 HashMap, HashSet, HashTable 같은 해시 기반 컬렉션에서 동작이 제대로 이루어지지 않는다. 즉 동일한 객체라도 다른 해시값을 가질 수 있게된다는 뜻이다.hashCode()를 재정의하지 않으면 기본적으로 Object의 hashCode()를 사용한다.Object의 기본 hashCode()는 객체의 메모리 주소를 기반으로 생성되므로, equals()를 재정의해도 논리적으로 같은 객체가 다른 해시값을 가질 수 있다.hashCode()를 재정의하지 않았을 때 발생하는 문제import java.util.HashMap;
public class PhoneNumber {
private final int areaCode;
private final int prefix;
private final int lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
this.areaCode = areaCode;
this.prefix = prefix;
this.lineNumber = lineNumber;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PhoneNumber)) return false;
PhoneNumber that = (PhoneNumber) o;
return areaCode == that.areaCode &&
prefix == that.prefix &&
lineNumber == that.lineNumber;
}
// hashCode() 미구현
public static void main(String[] args) {
HashMap<PhoneNumber, String> map = new HashMap<>();
PhoneNumber number1 = new PhoneNumber(123, 456, 7890);
map.put(number1, "Alice");
PhoneNumber number2 = new PhoneNumber(123, 456, 7890);
System.out.println(map.get(number2)); // null이 출력됨
}
}
number1과 number2는 같은 논리적 값을 가지지만 hashCode()가 없으므로 HashMap 내부에서 같은 객체로 인식하지 못해 null이 출력된다.hashCode() 재정의@Override
public int hashCode() {
return Objects.hash(areaCode, prefix, lineNumber);
}
Objects.hash()를 활용하면 간결하게 hashCode()를 재정의할 수 있다.hashCode() 작성 요령hashCode()는 같은 객체에 대해 항상 같은 값을 반환해야 한다.hashCode()는 항상 같은 값을 반환해야 한다.equals()가 같은 두 객체는 반드시 같은 hashCode를 가져야 한다.a.equals(b) == true이면 a.hashCode() == b.hashCode()여야 한다.hashCode()를 가질 수 있긴하다.hashCode()를 가질 필요는 없지만 다르면 좋다.HashMap, HashSet 등의 성능이 좋아지기 때문이다.기본적인 해시 계산 방식 (31을 사용한 해시 함수)
@Override
public int hashCode() {
int result = Integer.hashCode(areaCode);
result = 31 * result + Integer.hashCode(prefix);
result = 31 * result + Integer.hashCode(lineNumber);
return result;
}
Objects.hash() 사용
@Override
public int hashCode() {
return Objects.hash(areaCode, prefix, lineNumber);
}
Objects.hash()를 사용하면 내부적으로 적절한 해시 함수를 사용해 필드 값을 결합해준다.해시 코드 캐싱 (불변 객체일 때 최적화 가능)
private int hash; // 캐시된 해시값
@Override
public int hashCode() {
if (hash == 0) {
hash = Objects.hash(areaCode, prefix, lineNumber);
}
return hash;
}
String 클래스도 같은 방식으로 hashCode()를 캐싱한다.Objects.hashCode() 사용@Override
public int hashCode() {
return com.google.common.base.Objects.hashCode(areaCode, prefix, lineNumber);
}
Objects.hashCode()와 비슷하지만 더 정교한 해싱 알고리즘을 사용한다.@AutoValue
abstract class PhoneNumber {
abstract int areaCode();
abstract int prefix();
abstract int lineNumber();
static PhoneNumber create(int areaCode, int prefix, int lineNumber) {
return new AutoValue_PhoneNumberint areaCode, int prefix, int lineNumber);
}
}
@AutoValue를 붙이면 hashCode()를 직접 구현할 필요 없이 컴파일러가 자동으로 생성해 준다.Google Guava, AutoValue를 사용하면 더욱 안전하고 최적화된 hashCode를 생성할 수 있다.