Java - equals() 와 hashCode()

객체 비교(equals()) 객체 해시코드(hashCode())

equals와 hashCode의 관계를 알아보자


객체 비교(equals())

1
public boolean equals(Object obj) {...}

위에 보이는 코드 처럼 equals는 Object타입을 객체로 받는다 == 모든 객체 매개값으로 가능

기본적으로 equals는 ==비교연산자와 동일한 결과를 리턴한다 두 객체가 동일한 객체라면 true를 리턴하고 그렇지 않으면 false를 리턴한다.

1
2
3
4
5
Object obj1 = new Object();
Object obj2 = new Object();

boolean resultA = obj1.equals(obj1); //true
boolean resultB = (obj1==obj2); //true

equals() 메소드는 두 객체를 비교해서 논리적으로 동등하면 true를 리턴한다 여기서 논리적으로 동등하다라는 의미는 같은 객체이건 다른 객체이건 상관없이 객체가 저장하고 있는 데이터가 동일함을 뜻한다.

1
2
3
4
String s1 = "hi"
String s2 = "hi"

boolean result = (s1==s2); //true

String 객체의 번지를 비교하는 것이 나니고, 문자열이 동일한지를 조사하는 것 이런게 가능 한 것은 String.java 파일에 들어가 보면 equals를 재정의(오버라이딩)해서 번지 비교가 아닌 문자열 비교를 하기 때문

이런 재정의 방법은 다른 클래스에서도 사용이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Member.class
public class Member {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Member member = (Member) o;
        return **Objects.equals(getName(), member.getName())**;
    }
}

이런식이면 Member클래스 안에 name이 같은지 아닌지 알 수 있다.

1
2
3
4
5
6
7
Member memberA = new Member();
memberA.setName("hi");
Member memberB = new Member();
memberB.setName("hi");

System.out.println("(memberA == memberB) = " + (memberA == memberB)); //false
System.out.println("memberA.equals(memberB) = " + memberA.equals(memberB)); //true

객체 해시코드(hashCode())

  • 객체 해시코드란 객체를 식별할 하나의 정수값을 말한다.

Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가지고 있다.

HashSet, HashMap, Hashtable일 경우 hashCode()를 리턴받아 해시코드 값이 같으면 equals()리턴값을 비교해 true여야 같은 객체로 판별한다.

예를 들어 위에 Member.class를 보면 equals()를 재정의 해서 name의 값이 같으면 true를 리턴하도록 되어있다. 그런데 hashCode() 메소드는 재정의 하지 않았기 때문에 Object의 hashCode() 메소드가 사용된다.

이런 경우 HashMap의 식별키로 Member 객체를 사용하면 저장된 값을 찾아오지 못한다. 왜냐하면 Object의 hashCode()는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴 하기 때문에 각각의 객체가 다른 값을 가지고 있게 된다.

1
2
3
4
5
6
7
8
//Member 객체를 식별키로 사용해서 String 값을 저장하는 HashMap 객체 생성
HashMap<Member, String> hashMap = new HashMap<Member,Stirng>();

//식별키 new Member("hi")로 memberA를 저장
hashMap.put(new Member("hi"), "memberA");

//식별키 new Member("hi")로 memberA를 읽어옴
String value = hashMap.get(new Member("hi")); //null

결과로 null이 나오는 것처럼 member의 필드값이 같더라도 hashCode() 메소드에서 리턴하는 해시코드가 다르기 때문에 다른 식별키로 인식한다

그래서 의도한대로 “memberA”를 읽으려면 hashCode()를 재정의 해야한다. 이경우에는

1
2
3
4
@Override
public int hashCode() {
    return Objects.hash(getName());
}

new Member(“hi”)로 입력할 때와 new Member(“hi”)로 읽어올때는 서로 다른 객체이지만 HashMap의 HashCode()의 리턴값이 같고, equals()리턴값이 true가 나오기 때문에 동등한 객체로 평가한다.

결론적으로 equals()와 hashCode()를 같이 재정의 하자