equals()와 hashCode()

1. 정의

equals()는 오버라이딩 게시글에서 언급한 적 있다. equals()와 hashCode()는 둘 다 java.lang.Object 클래스의 메서드이다.

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

    // ...(생략)...

    @IntrinsicCandidate
    public native int hashCode();
}

equals()는 두 객체의 주소값을 비교한 결과를 boolean 값으로 리턴한다. hashCode()는 객체의 주소값을 int 값으로 리턴한다.

2. 오버라이딩

만일 두 객체의 주소값이 아닌 실제 데이터를 비교하고 싶으면 어떻게 해야 할까? equals() 메서드를 오버라이딩해서 구현해야 한다. 먼저 equals() 메서드를 오버라이딩 한 Coffee 클래스를 작성했다.

class Coffee {
    String name;

    Coffee(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Coffee coffee = (Coffee) o;
        return Objects.equals(name, coffee.name);
    }
}

그리고 Coffee 클래스의 객체를 생성하고 비교하기 위한 main() 메서드도 작성했다.

Ex1의 실행 결과는 어떻게 될까? 이름을 비교하도록 equals() 메서드를 오버라이딩 했으니 true를 반환할 것이다.

예상대로 coffee1과 coffee2는 필드 값이 같고 주소값이 다른 객체임을 알 수 있었다. 그런데 과연 필드 값만 같으면 두 객체는 논리적으로 동등할까? 헷갈린다면 아래 예제와 함께 생각해보자.

Ex2의 실행 결과는 어떻게 될까?

중복을 허용하는 List와 중복을 허용하지 않는 Set 둘 다 size로 2를 출력했다. 다시 말해서, 두 객체는 논리적으로 다른 것으로 취급되었다. Collection 객체에서 논리적으로 동등하다는 건 **equals()**의 결과값과 **hashCode()**의 결과값이 동일함을 말한다. 논리적으로 동등한 비교를 위해 Coffee 클래스에 hashCode()를 오버라이딩 해보자.

Ex2의 실행 결과는 어떻게 될까?

중복을 허용하는 List는 size로 2를 출력하지만, Set은 1을 출력했다. 또한 List의 hashCode도 동일한 값을 출력한다.

3. 주의 사항

equals()를 오버라이딩 하기 위해 참고할 주의 사항이 몇 가지 있다. 대부분은 IDE에서 자동으로 생성할테니 아래 내용은 참고하자.

  • equals()에 대한 일반 규약(참조 변수 x, y, z는 null이 아님)

    • 재귀적(reflexive): x.equals(x)는 true

    • 대칭형(symmetric): x.equals(y)가 true이면 y.equals(x)도 true

    • 전이성(transitive): x.equals(y)가 true이고 y.equals(z)가 true이면 x.equals(z)도 true

    • 일관성(consistant): 객체의 수정이 없는 경우 x.equals(y)를 여러 번 호출해도 결과값이 항상 일치

    • x.equls(null)은 false

  • hashCode()와 일관성 유지: equals()가 true이면 hashCode()도 true

  • 상속, 불변성, 성능을 고려한 구현

  • null 검사

Last updated