[JAVA] 자바 hashCode()

kindof

·

2021. 11. 20. 00:23

1. 자바 hashcode()

자바의 hashcode() 메서드는 Object 클래스의 메서드로써, 모든 클래스는 Object 클래스를 상속하기 때문에 사실 상 모든 객체에서 가지고 있는 메서드라고 볼 수 있습니다.

해시함수와 해시코드

 

그리고 이 hashCode() 메서드는 해싱 기법에 사용되는 해시함수(hash function)을 구현한 것인데요. 해시함수는 찾고자 하는 값을 입력하면 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환합니다.

 

 

2. equals()와 hashCode()

equals() 메서드 역시 Object 클래스가 가진 메서드입니다. equals는 매개변수로 객체의 참조변수를 받아서 비교하고, 그 결과를 boolean 값으로 리턴하는데요. 실제 Object 클래스 안에 equals 메서드는 아래와 같이 정의되어 있습니다.

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

위의 코드에서 알 수 있듯이 equals() 메서드는 두 객체의 같고 다름을 참조변수의 값으로 판단하는데요. 그렇기 때문에 서로 다른 두 객체로 equals 메서드로 비교하면 항상 false를 결과로 얻게 됩니다.

 

따라서, 두 객체가 같은지를 객체가 가진 필드값으로 판단하기 위해서는 equals() 메서드를 오버라이딩해서 정의해야 합니다.

 

예를 들어, Student 클래스의 객체가 이름과 학번을 가진다고 했을 때 이름과 학번이 모두 동일하다면 두 객체(학생)은 같은 학생이라고 볼 수 있으며 이를 위해서는 아래와 같이 Student 클래스 안에 equals() 메서드를 오버라이딩 해야하죠.

public class Student{
    int id;
    String name;
    
    // 생성자
    public Student(int id, String name){
        this.id = id;
        this.name = name;
    }
    
    // equlas 메서드 오버라이딩
    @Override
    public boolean equals(Object obj){
        if(obj instanceof Student){
            Student other = (Student) obj;
            return (this.name.equals(other.name) && this.id == other.id);
        }
    }
}

한편, 위 코드에서 '이름'을 비교할 때도 equals() 메서드를 사용했는데요. 이는 이름이 String 타입(레퍼런스 타입)으로 정의되어 있기 때문입니다.

 

위에서 만든 Student 클래스를 가지고 아래와 같이 두 객체가 같은지를 비교해보면 학번과 이름이 모두 같기 때문에 Student a, b는 같은 객체임을 볼 수 있습니다.

equals 결과는 같다

 

그런데, 이 부분에서 문제가 생깁니다. 학생 a, b가 같은 객체라면 a와 b에 대한 hashCode() 메서드도 같은 값을 리턴해야 하는데 hashCode()의 결과값은 각각 다른 값을 갖는다는 것입니다.

hashCode() 결과는 다르다

즉, equals()로 판별했을 때 두 객체가 같았는데 hashCode()로 판별했을 때 두 객체가 달라진다면 해싱의 의미가 사라져버리는 것이죠.

 

따라서 equals() 메서드를 오버라이딩할 때는 아래처럼 hashCode() 메서드도 이에 맞게 오버라이딩 해주는 것이 두 객체가 같다는 것을 보장하는 데 논리적인 결함이 없앨 수 있는 방법이 됩니다.

public class Student{
    int id;
    String name;
    
    // 생성자
    public Student(int id, String name){
        this.id = id;
        this.name = name;
    }
    
    // equlas 메서드 오버라이딩
    @Override
    public boolean equals(Object obj){
        if(obj instanceof Student){
            Student other = (Student) obj;
            return (this.name.equals(other.name) && this.id == other.id);
        }
    }
    
    // hashCode 메서드 오버라이딩
    @Override
    public int hashCode(){
        return id + name.hashCode();
}

equals(), hashCode() 모두 같다

 

3. String 타입과 hashCode()

String 클래스는 문자열의 내용이 달라도 동일한 hashCode()의 결과로 같은 값이 반환될 수 있는데요.

 

이는 hashCode()의 리턴 타입이 int이기 때문에 4byte로 표현 가능한 범위에서 결과가 나오지만 우리가 만들 수 있는 String 타입의 문자열은 너무나도 많기에 결국 중복되는 값이 생길 수 있기 때문이죠.

 

이로부터 알 수 있는 사실은 hashCode()가 같다고 해서 두 객체가 같은 객체임을 보장한다는 것은 아니라는 것입니다. 그래서 이를 면밀히 확인하기 위해서는 equals 메서드를 통해 두 String의 내용이 정확히 일치하는지를 확인해야 하겠습니다.

 

 

 


이렇게 이번 시간에는 자바의 hashCode() 메서드와 equals() 메서드에 대해 정리해보고 고민해보는 시간을 가졌습니다.

 

위에서 계속 한 이야기들을 요약하면 두 가지로 결론이 나옵니다.

 

1. equals() 메서드로 같은 객체는 같은 hashCode() 결과가 나와야 한다.

2. hashCode() 메서드로 같은 객체가 항상 equals() 메서드로 같은 객체는 아니다.

 

감사합니다.