equals()와 hashCode()
1. 정의
equals()는 오버라이딩 게시글에서 언급한 적 있다. equals()와 hashCode()는 둘 다 java.lang.Object 클래스의 메서드이다.
public class Object {
public boolean equals(Object obj) {
return (this == obj);
}
// ...(생략)...
@IntrinsicCandidate
public native int hashCode();
}
equals()는 두 객체의 주소값을 비교한 결과를 boolean 값으로 리턴한다. hashCode()는 객체의 주소값을 int 값으로 리턴한다.
2. 오버라이딩
만일 두 객체의 주소값이 아닌 실제 데이터를 비교하고 싶으면 어떻게 해야 할까? equals() 메서드를 오버라이딩해서 구현해야 한다. 먼저 equals() 메서드를 오버라이딩 한 Coffee 클래스를 작성했다.
class Coffee {
String name;
Coffee(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coffee coffee = (Coffee) o;
return Objects.equals(name, coffee.name);
}
}
그리고 Coffee 클래스의 객체를 생성하고 비교하기 위한 main() 메서드도 작성했다.
public class Ex1 {
public static void main(String[] args) {
Coffee coffee1 = new Coffee("아메리카노");
Coffee coffee2 = new Coffee("아메리카노");
if (coffee1.equals(coffee2)) {
System.out.println("같은 커피입니다.");
System.out.println(coffee1.hashCode());
System.out.println(coffee2.hashCode());
} else {
System.out.println("다른 커피입니다.");
System.out.println(coffee1.hashCode());
System.out.println(coffee2.hashCode());
}
}
}
Ex1의 실행 결과는 어떻게 될까? 이름을 비교하도록 equals() 메서드를 오버라이딩 했으니 true를 반환할 것이다.
# Ex1 실행결과
같은 커피입니다.
290658609
1577213552
예상대로 coffee1과 coffee2는 필드 값이 같고 주소값이 다른 객체임을 알 수 있었다. 그런데 과연 필드 값만 같으면 두 객체는 논리적으로 동등할까? 헷갈린다면 아래 예제와 함께 생각해보자.
class Ex2 {
public static void main(String[] args) {
Coffee coffee1 = new Coffee("아메리카노");
Coffee coffee2 = new Coffee("아메리카노");
List<Coffee> cafe1 = new ArrayList<Coffee>();
cafe1.add(coffee1);
cafe1.add(coffee2);
System.out.println(cafe1.size());
System.out.println(cafe1.get(0).hashCode());
System.out.println(cafe1.get(1).hashCode());
Set<Coffee> cafe2 = new HashSet<Coffee>();
cafe2.add(coffee1);
cafe2.add(coffee2);
System.out.println(cafe2.size());
for (Coffee c : cafe2) {
System.out.println(c.hashCode());
}
}
}
Ex2의 실행 결과는 어떻게 될까?
# Ex2 실행결과(첫 번째)
2
290658609
1577213552
2
290658609
1577213552
중복을 허용하는 List와 중복을 허용하지 않는 Set 둘 다 size로 2를 출력했다. 다시 말해서, 두 객체는 논리적으로 다른 것으로 취급되었다. Collection 객체에서 논리적으로 동등하다는 건 **equals()**의 결과값과 **hashCode()**의 결과값이 동일함을 말한다. 논리적으로 동등한 비교를 위해 Coffee 클래스에 hashCode()를 오버라이딩 해보자.
class Coffee {
String name;
Coffee(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coffee coffee = (Coffee) o;
return Objects.equals(name, coffee.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
Ex2의 실행 결과는 어떻게 될까?
# Ex2 실행결과(두 번째)
2
861563071
861563071
1
861563071
중복을 허용하는 List는 size로 2를 출력하지만, Set은 1을 출력했다. 또한 List의 hashCode도 동일한 값을 출력한다.
3. 주의 사항
equals()를 오버라이딩 하기 위해 참고할 주의 사항이 몇 가지 있다. 대부분은 IDE에서 자동으로 생성할테니 아래 내용은 참고하자.
equals()에 대한 일반 규약(참조 변수 x, y, z는 null이 아님)
재귀적(reflexive): x.equals(x)는 true
대칭형(symmetric): x.equals(y)가 true이면 y.equals(x)도 true
전이성(transitive): x.equals(y)가 true이고 y.equals(z)가 true이면 x.equals(z)도 true
일관성(consistant): 객체의 수정이 없는 경우 x.equals(y)를 여러 번 호출해도 결과값이 항상 일치
x.equls(null)은 false
hashCode()와 일관성 유지: equals()가 true이면 hashCode()도 true
상속, 불변성, 성능을 고려한 구현
null 검사
Last updated