IT 개발 라이프/Back_End

Java에서 equals()와 hashCode() 메서드 오버라이딩의 중요성 🚀

10Biliion 2024. 12. 6. 14:52

Java 개발을 하다 보면 equals()hashCode() 메서드를 오버라이딩해야 하는 상황이 자주 발생합니다. 두 메서드를 올바르게 오버라이딩하지 않으면 예기치 않은 동작이 발생할 수 있습니다. 이번 글에서는 두 메서드를 함께 오버라이딩해야 하는 이유를 구체적인 예제와 함께 설명합니다. ✍️


equals()와 hashCode()란 무엇인가? 🤔

  • equals() 메서드: 두 객체가 논리적으로 동등한지 비교합니다. 기본적으로 Object 클래스에서 제공하는 equals()는 두 객체의 참조값(메모리 주소)을 비교합니다.
  • hashCode() 메서드: 객체를 식별하는 정수 값을 반환합니다. 이 값은 해시 기반 컬렉션(예: HashMap, HashSet)에서 객체를 저장하고 검색하는 데 사용됩니다.

두 메서드의 관계 🔗

equals()hashCode()는 다음과 같은 규칙에 따라 동작해야 합니다:

  1. equals()가 true를 반환하는 두 객체는 동일한 hashCode() 값을 가져야 합니다.
  2. hashCode() 값이 같은 객체가 반드시 equals()에서 true를 반환할 필요는 없습니다.

이 규칙을 지키지 않으면 HashMap이나 HashSet과 같은 컬렉션에서 객체를 올바르게 처리하지 못합니다.


왜 두 메서드를 함께 오버라이딩해야 할까? ⚠️

1. hashCode()를 오버라이딩하지 않은 경우의 문제

import java.util.HashSet;

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);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Alice");
        Person p2 = new Person("Alice");

        HashSet<Person> set = new HashSet<>();
        set.add(p1);

        System.out.println(set.contains(p2)); // false
    }
}

문제 설명 📉

  • equals() 메서드를 오버라이딩했으므로 p1p2는 논리적으로 동등합니다.
  • 그러나 hashCode()를 오버라이딩하지 않았기 때문에 HashSetp2p1과 동일한 객체로 인식하지 못합니다.

2. equals()를 오버라이딩하지 않은 경우의 문제

import java.util.HashSet;

class Person {
    private String name;

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

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Alice");
        Person p2 = new Person("Alice");

        HashSet<Person> set = new HashSet<>();
        set.add(p1);

        System.out.println(set.contains(p2)); // false
    }
}

문제 설명 📉

  • hashCode()는 오버라이딩했으나 equals()를 오버라이딩하지 않았기 때문에 HashSet은 두 객체가 다르다고 판단합니다.

올바른 오버라이딩 예제 ✅

import java.util.HashSet;

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);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Alice");
        Person p2 = new Person("Alice");

        HashSet<Person> set = new HashSet<>();
        set.add(p1);

        System.out.println(set.contains(p2)); // true
    }
}

결과 설명 🌟

  • equals()hashCode()를 모두 오버라이딩했기 때문에 HashSetp1p2를 동일한 객체로 인식합니다.

결론 🎯

  1. equals()hashCode()는 항상 함께 오버라이딩해야 합니다.
  2. equals()는 객체의 논리적 동등성을 정의하고, hashCode()는 해시 기반 컬렉션의 효율적인 동작을 보장합니다.
  3. 두 메서드를 올바르게 오버라이딩하면 예기치 않은 동작을 방지하고, 코드의 신뢰성을 높일 수 있습니다.