본문 바로가기

Java

java - equals/hashcode

equals은 객체의 타입이 String인 경우를 제외하고

 

참조값을 동일한지를 판단한다.

 

 

CASE : String

StringMember member3 = new StringMember("kim");
StringMember member4 = new StringMember("kim");
String s = "kim";

System.out.println("member3.hashcode() = " + member3.hashCode());
System.out.println("member4.hashcode() = " + member4.hashCode());
System.out.println("s.hashCode() = " + s.hashCode());

System.out.println("객체간 비교 : member3.equals(member4) = " + member3.equals(member4));

/**
 * 두 경우 모두 String이기 때문에 값을 비교한다.
 */
System.out.println("객체의 값 비교 : member3.getName().equals(member4.getName()) = " + member3.getName().equals(member4.getName()));

/**
 * 이 경우도 마찬가지이다.
 */
System.out.println("StringMember의 객체의 name 필드 값과 String 비교 : member3.getName().equals(s) = " + member3.getName().equals(s));

 

member3.hashcode() = 1032616650
member4.hashcode() = 75457651
s.hashCode() = 106191
객체간 비교 : member3.equals(member4) = false
객체의 값 비교 : member3.getName().equals(member4.getName()) = true
StringMember의 객체의 name 필드 값과 String 비교 : member3.getName().equals(s) = true

 

equals는 기본적으로 객체의 참조값을 비교한다. 그렇기 때문에 new로 생성된 인스턴스인 member3/member4가 다르다고 판단한다. 하지만 객체의 타입이 String인 경우에는 내용을 비교한다.

그래서 StringMember 객체 인스턴스들의 name 필드(타입 String)들에 equals을 걸면 true라고 반환한다.

 


우리는 값을 비교하고 싶을때가 많다. 그래서 equals을 재정의한다. member3/member4가 동일하다고 하고 싶다.

그래서 equals을 재정의한다. 

 

StringMember 클래스의 equals 메서드를 아래와 같이 재정의했다고 해보자.

@Getter
class StringMember {
    private String name;

    public StringMember(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        StringMember m = (StringMember) o;
        return (this.getName() == m.getName());
    }
}

 

public class StudyEqualAndHashcode {

    public static void main(String[] args) {

        StringMember member3 = new StringMember("kim");
        StringMember member4 = new StringMember("kim");
        String s = "kim";

        /**
         * 이 경우 equals 메서드를 재정의 했기 때문에 멤버변수인 name의 참조값이 같으면 true를 반환함
         */
        System.out.println("객체의 값 비교 : member3.getName().equals(member4.getName()) = " + member3.getName().equals(member4.getName()));
        
    }
}

 

Output

객체의 값 비교 : member3.getName().equals(member4.getName()) = true

 

좀 전과 다르게 객체의 값을 비교해도 true라고 뜬다.

하지만 이 경우 치명적인 문제가 있다.

 

객체가 동일하다고 판단하지만 사실은 두 객체의 멤버변수인 name 같은 것이다.

그래서 객체는 서로 다른 주소값을 가지고 이로 인해 서로 다른 해시코드를 가진다. 그래서 동일 객체를 제외할 수 있는 Set같은 경우 문제가 생기게 된다.

 

Set<StringMember> memberSet = new HashSet<>();
memberSet.add(member3);
memberSet.add(member4);
System.out.println(memberSet.size());

HashSet<Integer> hashSet = new HashSet<>();
hashSet.add(1);
hashSet.add(1);
System.out.println(hashSet.size());

우리는 위에서 equals 메서드를 재정의했기 때문에 memberSet의 size 1을 기대할 것이다. 하지만 결과는 2이다.

 

Output

2
1

 


오류를 바로잡기 위해서는 해시코드 메서드를 재정의해야한다.

 

@Override
public int hashCode() {
    final int PRIME = 31;
    return PRIME * getName().hashCode(); //올바른 사용법인지는 잘 모르겠다..
}

 

Output

1
1

 

잘못된 내용이 있다면 지적 부탁드립니다.