본문 바로가기

CS

equals()와 hashCode() 재정의

Java에서 객체를 비교할 때 자주 사용하는 equals()와 hashCode()는 한 번쯤은 제대로 짚고 넘어가야 하는 주제입니다. 처음에는 단순해 보이지만,

 

실제로는 지켜야 할 규칙도 많고 생각보다 까다로운 부분도 있습니다. 이 글에서는 equals와 hashCode의 기본 동작부터 주의할 점, 실수하기 쉬운 사례까지 차근차근 정리해보았습니다.


Object 클래스의 기본 equals 동작 

 

 

equals 메서드는 Java에서 객체 간의 동등성을 비교하기 위해 사용하는 메서드로 기본적으로 모든 Java 객체는 Object 클래스를 상속받기 때문에, equals 메서드는 모든 객체에 포함되어 있습니다.

 

Object 클래스의 equals 메서드는 기본적으로 참조(레퍼런스)가 같은지를 비교합니다. 즉, 두 객체가 동일한 메모리 주소를 가리키는지 확인합니다.

 

 

public boolean equals(Object obj) {
    return (this == obj);
}

 

 

 

이 기본 구현은 메모리 상에서 같은 객체인지 확인하는 데 적합하지만, 객체의 내용을 비교하려는 경우에는 적합하지 않습니다. 대부분의 경우, 객체의 참조가 아니라 속성 값이 같은지를 비교하고 싶을 때가 많으며

 

 

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return name.equals(person.name);
    }
}

 

 

예를들어 Person 이라는 객체에서 이름이 같으면 같은 객체로 간주하고 싶다면 equals를 재정의해야 합니다.  위와 같이 equals() 메서드를 오버라이드하면, Person 객체의 name 필드 값을 기준으로 참조 값을 비교하는 것이 아닌 동등성을 비교할 수 있습니다.

 


hashCode() 메서드란?

 

hashCode()는 객체를 해시 기반 자료구조(HashSet, HashMap, HashTable 등) 에 사용할 때, 객체를 빠르게 찾기 위한 정수형 해시값을 반환하는 메서드입니다

 

public int hashCode()

 

기본적으로 Object 클래스는 객체의 메모리 주소를 기반으로 해시값을 생성합니다. 즉, equals()와 마찬가지로 참조 동일성에 의존하는 방식입니다.

 


왜 equals()와 hashCode()는 함께 재정의해야 할까?

 

 

자바에서 HashMap, HashSet과 같은 해시 기반 자료구조는 데이터를 저장하거나 검색할 때 내부적으로 해시 함수를 사용합니다. 이 해시 함수는 객체의 hashCode() 값을 기반으로 데이터를 특정 버킷(Bucket) 에 배치합니다.

 

이 과정을 이미지로 보면, 각 키는 먼저 해시 함수를 통과하고, 그 결과로 나온 해시값에 따라 다른 위치의 버킷에 분배됩니다. Mark Bell이 123-9876 버킷에, John Reese는 123-7654에, Michelle Curtis는 123-1234에 저장되는 식입니다.

 

이때 중요한 점은, 동일한 키라고 간주되는 객체라면 항상 같은 버킷에 들어가야 한다는 것입니다. 그래야 검색할 때 정확한 위치에서 값을 찾을 수 있습니다.

 

따라서 객체가 논리적으로 같다면, 즉 equals() 메서드로 비교했을 때 true가 나와야 한다면, 반드시 hashCode() 값도 같아야 합니다. 그렇지 않으면 서로 다른 버킷에 저장되어, 실제로는 같은 객체임에도 찾을 수 없는 문제가 발생합니다. 이런 이유로 자바에서는 equals()를 재정의할 경우, 반드시 hashCode()도 함께 재정의해야 한다고 규약으로 정하고 있는 것입니다. 해시 기반 자료구조의 정확성과 신뢰성을 유지하기 위한 핵심 원칙입니다.

 

 

Person p1 = new Person("Jinyoung");
Person p2 = new Person("Jinyoung");

Set<Person> set = new HashSet<>();
set.add(p1);
System.out.println(set.contains(p2)); // false! (equals는 true인데, hashCode는 다름)

 

equals()는 true를 반환하더라도, hashCode()가 다르면 서로 다른 버킷에 들어가서 탐색이 되지 않는다.