[Java] equals와 hashCode



 

 

개요

 "자바에서 객체의 동등성 비교를 위해서는 equals() 메서드가 사용되며, equals()메서드를 오버라이딩해줄 경우 hashCode()메서드도 오버라이딩해 주어야 한다. 단, 역은 성립하지 않는다"

 

자바를 처음 공부했을 때 equals와 hashCode라는 메서드에 대해서 나는 이정도로만 이해하고 있었다. 그런데 여러 프로젝트를 진행하면서도 equals를 오버라이딩 할 일이 없어서 한번 실습하면서 정리해보려고 한다.

 

equals와 hashCode


앞서 이야기했듯이 equals는 참조 변수의 동등성을 비교하는 함수이다. 최상위 클래스인 Object 클래스를 들어가보면 equals() 메서드를 확인할 수 있는데, 위와 같이 '=='연산을 통해 단순히 두 객체의 참조(주소)가 같은지를 비교하고 있다.

 

반면, String 클래스는 위와 같이 equals() 메서드를 오버라이딩하여 직접 문자열이 같은지를 확인하고 있다.

 

String s1 = "this";
String s2 = "this";
String s3 = new String("this");
String s4 = new String("this");

System.out.println(s1==s2); // true
System.out.println(s1.equals(s2)); // true
System.out.println(s3==s4); // false
System.out.println(s3.equals(s4)); // true

 

 따라서 s1부터 s4의 문자열을 equals() 메서드를 통해 비교하면 true라는 결과가 리턴된다. 하지만 리터럴을 통해 선언된 s1과 s2는 동일한 메모리(힙 메모리의 String Pool)를 참조하기 때문에 ==비교의 결과도 참인 반면, new 키워드를 통해 선언된 s3과 s4는 String Pool이 아닌 힙 메모리를 직접 참조하기에 주소값이 달라 ==비교 결과 거짓이 된다.

 

 

equals와 hashCode 커스텀하기

다들 이정도의 개념은 알테니 어떻게 equals를 활용할 수 있는지 실습해보자.

public class Main {
    public static void main(String[] args) {
        Node i1 = new Node(1, 2, 300);
        Node i2 = new Node(1, 2, 400);

        System.out.println(i1.equals(i2)); // false
    }

    static class Node {
        int x;
        int y;
        int val;
        public Node(int x, int y, int val) {
            this.x = x;
            this.y = y;
            this.val = val;
        }
    }
}

 

Node라는 커스텀 객체를 만들고, x와 y값이 모두 같다면 동등 판정을 하고 싶다면, 다음과 같이 equals()를 오버라이딩해주면 된다.

 

@Override
public boolean equals(Object o) {
    if (o == null || this.getClass() != o.getClass()) return false; 
    Node compare = (Node)o;
    return this.x==compare.x && this.y==compare.y;
}

먼저 객체의 타입이 일치하는지를 확인하고, 일치한다면 equals()를 오버라이딩해주면 된다.

 

equals()를 오버라이딩했으니 이제 hashCode()도 오버라이딩해주어야 한다. hashCode()는 객체의 해시 코드를 반환하는 메서드로, HashSet이나 HashMap과 같은 해시 기반 컬렉션에서 객체의 동등성을 판단하는데 사용된다. 

 

@Override
public int hashCode() {
    int result = Integer.hashCode(x); // x의 해시 코드
    result = 31 * result + Integer.hashCode(y); // y의 해시 코드 추가
    return result; // 최종 해시 코드 반환
}

Integer.hashCode()함수는 자바에서 int값의 해시코드를 받을 수 있는 static 메서드이다. 이를 적절히 x와 y를 이용하여 해시값을 생성해주면 된다. 여기서는 x의 해시코드에 31이라는 소수값을 곱하고, 다시 y의 해시코드를 더해주는 방식으로 해시값을 구했다.

 

 

 

hashCode를 오버라이딩해야 하는 이유 (동작 순서)

https://inpa.tistory.com/entry/JAVA-%E2%98%95-equals-hashCode-%EB%A9%94%EC%84%9C%EB%93%9C-%EA%B0%9C%EB%85%90-%ED%99%9C%EC%9A%A9-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0#hashcode_%EC%99%80_equals_%EB%8F%99%EC%9E%91_%EC%88%9C%EC%84%9C

 

 해시를 사용하는 컬렉션에서는 객체의 동등성을 비교할 때, hashCode()의 리턴값을 확인한 후, 같다면 추가적으로 equals()를 확인하는 방식으로 동작한다.

 

Object클래스의 hashCode 메서드는 객체의 고유 주소값을 int값으로 변환하기 때문에 객체마다 다른 값을 리턴한다. 따라서 둘 중에 하나라도 다르다면 다른 객체로 취급되기에, equals를 오버라이딩해줄 경우 해시 컬렉션 사용에 대비하여 hashCode()도 오버라이딩하는 것이 좋다는 것이다.