Java는 사용자 정의 클래스를 HashMap의 키 값으로 사용합니다.

이것은 자바에서 매우 고전적인 문제로 면접에서도 자주 묻는다.사실 많은 책이나 글에서hashCode()와 equals() 두 가지 방법을 다시 불러와야 사용자 정의 키를 HashMap에서 찾을 수 있다고 언급했지만 왜 이러는지, 그리고 그렇지 않으면 어떤 결과가 생길지 설명하는 글은 드물 것 같아서 이렇게 한 편을 써서 설명합니다.
우선, 만약에 우리가 아래의 Person 클래스를 키로 해시맵에 저장하면 어떤 상황이 발생합니까?

public class Person {

  private String id;

  public Person(String id) {
    this.id = id;
  }
}
import java.util.HashMap;

public class Main {
  public static void main(String[] args) {

    HashMap<Person, String> map = new HashMap<Person, String>();

    map.put(new Person("001"), "findingsea");
    map.put(new Person("002"), "linyin");
    map.put(new Person("003"), "henrylin");
    map.put(new Person("003"), "findingsealy");

    System.out.println(map.toString());

    System.out.println(map.get(new Person("001")));
    System.out.println(map.get(new Person("002")));
    System.out.println(map.get(new Person("003")));
  }
}
그러면 출력 결과는 뭘까요?

{Person@6e4d4d5e=henrylin, Person@275cea3=findingsea, Person@15128ee5=findingsealy, Person@4513098=linyin}
null
null
null
우리는 여기에서 두 가지 문제가 발생한 것을 볼 수 있다.
1. 추가하는 과정에서 키=New Person(〃003)의 키 값을 두 번 추가했습니다. 그러면 기대에서 HashMap에는 한 쌍의 키 값 쌍만 존재해야 합니다. 키(기대 중)는 같기 때문에 중복 추가하지 말아야 합니다. 두 번째 추가된 value ='findingsealy'는 원래의 value ='henrylin'을 교체해야 합니다.그러나 입력에서 우리는 기대했던 상황이 나타나지 않고 HashMap에value='findingsealy'와'value='henrylin'의 두 키 값이 일치하고 그들의 키 값이 같지 않다는 것을 발견했다. 이것은 분명히 잘못된 것이다.
2.value 값을 얻을 때 우리는 각각 세 개의 Person 대상으로 찾습니다. 이 세 개의 대상은 우리가 방금 저장한 세 개의 키 값과 같지만 세 개의null 값을 찾습니다. 이것은 분명히 잘못된 것입니다.
그렇다면 정확한 방법은 사실 많은 곳에서 묘사된 바와 같이 Person 클래스를 직접 수정하고 equals와hashCode 방법을 다시 불러옵니다. 수정된 Person 클래스는 다음과 같습니다.
 

public class Person {

  private String id;

  public Person(String id) {
    this.id = id;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Person person = (Person) o;

    if (id != null ? !id.equals(person.id) : person.id != null) return false;

    return true;
  }

  @Override
  public int hashCode() {
    return id != null ? id.hashCode() : 0;
  }
}
그러면 위의 검사 절차를 다시 실행할 때 얻은 결과는 다음과 같습니다.
 

{Person@ba31=findingsea, Person@ba32=linyin, Person@ba33=findingsealy}
findingsea
linyin
findingsealy
앞서 지적한 하이라이트 오류가 모두 수정된 것으로 보인다.그럼, 왜 그랬을까?
HashMap에서 키를 찾는 비교 순서는 다음과 같습니다.
1. 객체의 Hash Code를 계산하여 테이블에 있는지 확인합니다.
2. Hash Code 위치에 있는 객체와 현재 객체가 동일한지 확인합니다.
분명히 첫 번째 단계는hashCode () 방법이고, 두 번째 단계는equals () 방법을 사용해야 한다.재부팅이 진행되지 않을 때 이 두 단계에서는 Object 클래스의 이 두 가지 방법을 기본적으로 호출합니다. Object에서 Hash Code의 계산 방법은 대상의 주소에 따라 계산됩니다. 두 Person("003")의 대상 주소는 다르기 때문에 Hash Code도 다르기 때문에 자연히 Hash Map도 그것들을 같은 키라고 생각하지 않습니다.또한 Object 기본 equals()에서도 객체의 주소에 따라 비교할 때 자연히 한 Person(""003"")과 다른 Person(""003"")은 같지 않습니다.
이 점을 이해하면hashCode()와 equals 두 가지 방법을 동시에 불러와야 하는 이유를 쉽게 알 수 있습니다.
• 해시코드 () 를 다시 불러오는 것은 같은 키에 대해 같은 해시코드를 얻을 수 있도록 하기 위해서입니다. 이렇게 하면 해시맵이 우리가 지정한 키에 위치할 수 있습니다.
• equals () 를 다시 불러오는 것은 HashMap에 현재 대상과 키에 저장된 대상이 같다는 것을 표시하기 위해서입니다. 이렇게 해야만 우리는 이 키에 대응하는 이 키 값을 얻을 수 있습니다.
Person 클래스에서 hashCode () 에 대한 재설정 방법은 다음과 같습니다.
 

@Override
public int hashCode() {
  return id != null ? id.hashCode() : 0;
}
여기서 궁금한 점은 왜 String 유형의 변수인 Hash Code를 Person 유형의 Hash Code 값으로 사용할 수 있느냐는 점이다.이렇게 new Person("new String")과 new Person("new String")의 Hash 코드는 동일합니까?
다음 코드의 출력을 살펴보겠습니다.

System.out.println("findingsea".hashCode());
System.out.println("findingsea".hashCode());
System.out.println(new String("findingsea").hashCode());
System.out.println(new String("findingsea").hashCode());
728795174
728795174
728795174
728795174
네 개의 문장의 출력이 모두 같다는 것을 볼 수 있다. 직관적이고 합리적인 추측은 String 유형도hashCode()를 다시 불러와 문자열의 내용에 따라 Hash Code 값을 되돌려주기 때문에 같은 내용의 문자열은 같은Hash Code를 가지고 있다는 것이다.
또한 이것은 문제를 설명한다. 왜 이미 알고 있는hashCode()가 같은 상황에서 equals()로 비교해야 하는가?바로 상술한 예에서 나타난 상황을 피하기 때문이다. Person 클래스에 대한hashCode() 방법의 재부팅 실현에 따라 Person 클래스는 id라는 String 유형의 구성원의 Hash Code 값을 자신의 Hash Code 값으로 직접 사용하지만 분명히 한 Person(〃003)과 한 String(〃003)은 같지 않기 때문에hashCode()가 같은 상황에서 equals()로 비교해야 한다.
다음 예는 상기 설명의 증거로 삼을 수 있다.

System.out.println(new Person("003").hashCode()); // 47667
System.out.println(new String("003").hashCode()); // 47667

System.out.println(new Person("003").equals(new String("003"))); // false
상기 자바는 사용자 정의 클래스를 HashMap의 키 값으로 사용한 실례가 바로 편집자가 여러분에게 공유한 모든 내용입니다. 여러분에게 참고가 되고 저희를 많이 사랑해 주시기 바랍니다.

좋은 웹페이지 즐겨찾기