[Java]hashcode( )

Wintering·2022년 6월 5일
0

이펙티브 자바

목록 보기
15/18

hashcode()

hashcode는 각 객체의 주소값을 변환하여 생성한 각 객체의 고유한 정수값이다.
  • equals( ) : 두 객체의 내용이 같은지 확인하는 메소드
  • hashcode( ) : 두 객체가 동일한 객체인지 확인하는 메소드

package effectivejava;

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("kim");
        Person person2 = new Person(new String("kim"));
        Person person3 = person2;
        System.out.println(person1.hashCode());     //356573597
        System.out.println(person2.hashCode());     //1735600054
        System.out.println(person3.hashCode());     //1735600054
    }
}

class Person {
    String name;

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

    @Override
    public boolean equals(Object obj) {
        Person anotherObj = (Person) obj;
        return this.name.equals(anotherObj.name);
    }
}

person1과 person2는 같은 "kim" 이란 문자열을 갖지만 완전히 다른 객체다.
person1은 "kim"이란 문자열을 name값으로 갖는 새로운 Person객체를 생성한 것이고,
person2는 "kim"이란 String 객체를 name값으로 갖는 새로운 Person객체를 생성한 것이기 때문이다.

hashcode()역시 다른데, hashcode()는 각 객체가 참조하는 주소값을 기반으로 값을 리턴하기 때문이다. 이 때 person3은 person2와 결국 같은 객체이므로 같은 주소를 참조하여 hashcode()값도 같아진다.


String 객체의 hashcode()

  • String의 equals()가 재정의되어있듯이, String의 hashcode()역시 재정의 되어있다.
    한글자씩 가져와서 정수값을 더하고 있다. 즉 String에서는 주소값이 아닌, 문자열 그 자체를 비교하여 같은 hashcode값을 낼 수 있도록 하고있다.

hashcode와 Hash관련 collection?

hashcode가 객체의 유일한 값이 아니라면 문제가 생기지 않을까?

Hashmap의 경우를 생각해봤을 때, hashmap의 key는 hashcode값을 기준으로 정해진다. 서로 다른 객체의 hashcode가 같다면 중복 key가 된다. 그런데 Map의 특성 상 key는 중복되지 않는다. 그래서 HashMap의 경우 서로 다른 객체의 같은 hashcode값에 대해서도 적절한 처리가 될 수 있도록 구성되어있다.

String a와 String b가 hashcode는 같은데 서로 다른 문자열이라면 (즉, equals()가 false라면) 사용자는 직관적으로 둘이 다른 문자열임을 인지할 수 있고, 바로 이런 경우를 고려해야 하는 것이다.

ex)
String a = "Z@S.ME";
String b = "Z@RN.E";

그러나 반대의 경우 즉, equals()는 true이고, 사용자가 직관적으로 판단했을 때도 같은데, hashcode()가 false인 경우는 어떡해야할까? hashcode()를 굳이 검사하기 전까지는 둘을 동일한 key를 가졌다고 판단하겠지만 그렇지 않은 것이다.

따라서 equals()와 hashcode()의 결과가 같도록 권장하고 있고, 이렇기 때문에 equals()의 결과와 hashcode()의 결과가 같을 수 있도록 hashcode()를 재정의해야한다.


package effectivejava;

import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        String a = "Z@S.ME";
        String b = "Z@RN.E";

        HashMap<String, Integer> hasnMap2 = new HashMap<>();
        hasnMap2.put(a, 30);
        hasnMap2.put(b, 40);

        System.out.println(a.equals(b));
        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
        System.out.println(hasnMap2.size());
        System.out.println(hasnMap2.get(a));
        System.out.println(hasnMap2.get(b));

        System.out.println("------------------------");

        Person person1 = new Person("kim");
        Person person2 = new Person(new String("kim"));

        HashMap<Person, Integer> hashMap = new HashMap<>();
        hashMap.put(person1, 10);
        hashMap.put(person2, 20);
        
        System.out.println(person1.equals(person2));
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
        System.out.println(hashMap.size());
        System.out.println(hashMap.get(person1));
        System.out.println(hashMap.get(person2));
    }
}

class Person {
    String name;

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

    @Override
    public boolean equals(Object obj) {
        Person anotherObj = (Person) obj;
        return this.name.equals(anotherObj.name);
    }
}
  • hashmap2의 경우)
    hashcode는 같지만 hashMap에서는 다른 키로 구분해주고 있음.
    문자열이 다르므로 당연히 equals()는 false

  • hashmap의 경우)
    person객체에서 equals를 재정의하여 true
    하지만 person1과 person2는 서로 참조하는 주소값이 다르기때문에 당연히 hashcode값은 다르고, 동일한 key가 아니기 때문에 hashMap의 크기가 2가 된다. (어차피 저장되는 value는 "kim"으로 동일한데)

위와 같은 문제점을 해결하기 위해 hashmap도 재정의해줘야한다.

@Override
public int hashCode(){
	int hashCode = 0;
    hashCode = 31 * hashCode + ((name == null) ? @ : name.hashCode());
    return hashCode;
}

결론
equals()false이고, hashcode() true인 경우 : hashMap은 다른 Key로 처리
equals()true이고, hashcode() false인 경우 : hashMap은 다른 Key로 처리
equals()true이고, hashcode() true인 경우 : hashMap은 같은 Key로 처리

0개의 댓글