equals와 hashCode 오버라이딩

2025. 3. 11. 08:54·Language/Java
반응형

equals와 hashCode

Java에서 `equals()`와 `hashCode()` 메서드는 모든 Java 객체의 부모 객체인 Object 클래스에 정의되어 있다.

때문에 모든 객체는 해당 메서드를 상속 받고 있다. 이는 객체들의 동등성을 비교할 때 사용된다.

 

`equals()`는 같은 객체인지(메모리 주소가 같은지) 확인하는 기능만 제공한다. (즉 `==`와 동일한 동작을 한다.)

`hashCode()`는 객체를 식별하는 정수값(해시코드)를 반환하는 메서드이다.

 

🚨 중요한 규칙

`equals()`가 `true`이면 `hashCode()`는 무조건 `true`이다.

하지만 `hashCode()`가 `true`여도 `equals`는 `false`일 수 있다. ⬅ 해시 충돌이 발생할 수 있기 때문.

 

equals

일반적인 객체에서의 equals 예시

아래는 `equals()`메서드 사용 예시이다.

Person p1 = new Person("Jeny");
Person p2 = new Person("Jeny");
Person p3 = new Person("Lisa");
System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p3)); // false

 

`p1`과 `p2`는 `new Person()`을 사용하여 각각 다른 메모리 공간에 생성된 객체이다.

`equals`는 메모리 주소를 비교하기 때문에 `p1.equals(p2)`는 `false`를 반환한다.

 

 

String 객체에서의 equals 예시

String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true

❌  분명 `new`를 사용하여 새로운 객체를 생성하였으므로 s1과 s2는 서로 다른 참조값을 가진다. 때문에 `s1.equals(s2)`는 false가 나와야하지만, 결과는 true이다.

❗️`String`클래스는 기본적으로 `equals()`를 오버라이딩하여 문자열의 내용을 비교하도록 구현되어 있기 때문에, `s1.equals(s2)`가 true를 반환하는 것이다.

 

 

equals의 Override

`String`클래스와 같이 객체의 내용으로 비교하도록 구현하고 싶다면 `equals()` 메서드를 오버라이딩해야 한다.

class Person {
    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`객체의 name을 비교하고, name이 같은 문자열이면 `true`를 반환하도록 오버라이딩한 것을 확인할 수 있다.

Person p1 = new Person("Jeny");
Person p2 = new Person("Jeny");
Person p3 = new Person("Lisa");
System.out.println(p1.equals(p2)); // true
System.out.println(p1.equals(p3)); // false

 

때문에 첫번째 예시에서의 결과와 달리, `p1`과 `p2`의 name이 같기 때문에 `p1.equals(p2)`는 `true`를 반환한다.

`p1.equals(p3)`는 name이 서로 다르기 때문에 `true`를 반환한다.

 

 

hashCode

hashCode의 필요

`hashCode()`는 `HashSet`, `HashMap`, `HashTable`과 같은 해시 기반 컬렉션에서 주로 사용된다.

이는 빠른 검색과 삽입을 가능하게 하고, 해시값을 기반으로 "버킷"을 찾아 빠르게 접근할 수 있다.  

`hashCode()`가 충돌을 최소화하는 방식으로 구현되면, 검색 속도가 O(1)에 가까워진다.

 

 

hashCode의 Override

위의 코드를 실행하면 아래와 같은 경고가 뜰 수 있다.

Warning: Overriding 'equals' without overriding 'hashCode'

 

말 그대로, `hashCode()`는 오버라이딩 하지 않고 `equals()`만 오버라이딩 했다는 경고 문구이다.

실제로, Java의 Object 문서에는 🚨`equals()`를 오버라이딩하면 `hashCode()`도 함께 오버라이딩 해야한다고 명시하고 있다.

이는 컬렉션(`HashSet`, `HashMap`, `HashTable`)에서 객체를 비교할 때 `hashCode()`가 사용되기 때문이다.

 

아래는 `Person`객체에서 `equals()` 메서드만 오버라이딩 되었을 때 `HashSet`을 사용하는 예시이다.

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

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

System.out.println(p1.equals(p2)); // true
System.out.println(set.contains(p2)); // false

❌  p1과 p2는 같은 name을 가지고 있으므로 논리적으로 같은 객체이다(`p1.equals(p2) == true`). 하지만 `set`에 `p1`을 추가하고 `p2` 포함 여부를 살피면 `false`가 반환된다.

❗️이는 `hashCode()`를 오버라이딩 하지 않아 서로 다른 값이 반환되어 `HashSet`이 같은 객체로 인식하지 않기 때문이다.

 

이를 해결하기 위해 Person 객체의 hashCode()메서드를 오버라이딩한다.

import java.util.Objects;

class Person {
    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 Objects.equals(name, person.name);
    }

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

 

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

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

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

다시 `set`에 `p1`을 추가하고 `p2` 포함 여부를 살피면 `true`가 반환된다.

`hashCode()`가 `name`의 `hash`를 반환하도록 오버라이딩 했기 때문에, `name`이 같으면  논리적으로 같은 객체라고 판단한다.

 

 

마무리

`equals()`는 두 객체의 논리적 동등성을 비교하는데 사용된다. 내용 기반으로 객체를 비교할 수 있도록 해준다.

`hashCode()`는 해시 기반 컬렉션에서 성능을 최적화하는 데 중요한 역할을 한다. 효율적인 검색과 빠른 데이터 처리를 가능하게 해준다.

실제 프로젝트에서 equals()와 hashCode()를 제대로 오버라이딩하지 않으면, 해시 기반 컬렉션(HashSet, HashMap, HashTable 등)을 사용할 때 예상치 못한 버그나 성능 저하가 발생할 수 있다. 예를 들어, 중복된 객체가 컬렉션에 추가되거나, 객체가 제대로 검색되지 않는 문제가 발생할 수 있다.

 

728x90
반응형

'Language > Java' 카테고리의 다른 글

Java 기술 면접 대비  (0) 2025.03.20
[JSP] JSP(JavaServer Pages)의 정의와 기본 구성 요소  (1) 2025.01.02
record 클래스 (Java 14)  (0) 2024.12.13
직렬화(Serialization)  (0) 2024.12.12
동기화(synchronization)와 락(Lock)  (1) 2024.12.11
'Language/Java' 카테고리의 다른 글
  • Java 기술 면접 대비
  • [JSP] JSP(JavaServer Pages)의 정의와 기본 구성 요소
  • record 클래스 (Java 14)
  • 직렬화(Serialization)
settong
settong
    250x250
  • settong
    개 발 자 국
    settong
  • 전체
    오늘
    어제
    • 전체보기 (202)
      • Computer Science (50)
        • Network (7)
        • Operating System (18)
        • Data Structure (9)
        • Database (11)
        • Algorithm (5)
      • Language (17)
        • Java (17)
        • Javascript (0)
        • Python (0)
      • Devops (20)
        • AWS (0)
        • Naver Cloud (16)
        • CICD (3)
        • 웹 서버 관리 (1)
      • Front (0)
        • React (0)
      • Backend (5)
        • Spring (5)
      • 코딩 테스트 정복기 (110)
        • 백준 (51)
        • 프로그래머스 (53)
        • 기타 (6)
      • etc (0)
      • 경제 상식 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    벨만포드
    CI/CD
    프로그래머스
    해시
    다이나믹프로그래밍
    github actions
    ncp200
    다익스트라
    Network
    집합
    lcs
    Spring Boot
    ncp
    DFS
    백준
    백트래킹
    완전탐색
    ncp202
    BFS
    분할정복
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
settong
equals와 hashCode 오버라이딩
상단으로

티스토리툴바