equals() 와 hashcode() 없이는 상당히 많은 if 문을 객체의 모든 field를 비교하기 위해서 사용해야 한다.
Java에서 객체 비교를 효율적이고 쉽게 해주는 equals와 hashcode()에 대해서 공부해 보자.
equals()와 hashcode()는 모든 Java 객체의 상위 클래스인 Object Class에 정의되어 있다.
모든 java class는 equals()와 hashcode()를 포함하고 있지만 제대로 작동하기 위해서는 Overriding 해야 한다.
Overriding 이 equals() 및 hashcode()에서 어떻게 작동하는지 확인해 보자.
public boolean equals(Object obj) {
return (this == obj);
}
@HotSpotIntrinsicCandidate
public native int hashCode();
위 메서드가 대신 호출됨.
이 경우에는 equals() 와 hashcode()의 진짜 목적인 두 개 이상의 객체가 동일한 값을 갖는지 확인하는 것을 수행하지 않습니다.
return (this == obj);
equals()를 재정의 하는 경우 hashcode()는 항상 재정의 하는것이 좋다.
Java에서 objects를 비교하기 위해서 equals() method를 사용할 때
equals()는 object의 속성값을 비교한다.
public class EqualsAndHashCodeExample {
public static void main(String... equalsExplanation) {
System.out.println(new Simpson("Homer", 35, 120)
.equals(new Simpson("Homer",35,120)));
System.out.println(new Simpson("Bart", 10, 120)
.equals(new Simpson("El Barto", 10, 45)));
System.out.println(new Simpson("Lisa", 54, 60)
.equals(new Object()));
}
static class Simpson {
private String name;
private int age;
private int weight;
public Simpson(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Simpson simpson = (Simpson) o;
return age == simpson.age &&
weight == simpson.weight &&
name.equals(simpson.name);
}
}
}
1번째 비교
System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120)));
2번째 비교
System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45)));
마지막으로 Simpson 과 object 클래스 비교
System.out.println(new Simpson("Lisa", 54, 60).equals(new Object()));
== 연산자와 equals() method가 겉으로 보이기에 좀 비슷해 보일 수 있을지 모르나.
사실은 다르게 작동한다.
== 연산자는 두 개의 object 가 같은 object를 참조하는지 (references) 를 비교한다.
// 1번비교에서 생성한 simpson으로 homer 인스턴스 2개를 만들었다고 가정
System.out.println(homer == homer2);
1번째 비교에서 new 연산자를 사용하여 두 개의 서로 다른 인스턴스를 만들었고
new 연산자 때문에 homer와 homer2는 서로 다른 memory heap 영역을 참조하고 있기 때문에 결과 값이 false입니다.
equals() 메서드를 재정의한 경우
System.out.println(homer.equals(homer2));
object를 비교할 때 performance, 성능 최적화를 하기 위해 hashcode()를 사용한다.
hashcode()는 object의 유일한 id 값을 리턴해주고 object의 전체 상태를 더욱 쉽게 비교할 수 있게 만들어준다.
object의 hashcode가 또 다른 object의 hashcode와 같지 않다면 equals() 매서드를 실행시킬 이유가 없다.
하지만 만약에 두 object가 같지 않은데 hashcode가 만약에 같다면
values값, 필드 값들이 같은지 아닌지를 확인하기 위해 반드시 equals() 매서드를 재정의 해서 실행시켜야 한다.
public class HashcodeConcept {
public static void main(String... hashcodeExample) {
Simpson homer = new Simpson(1, "Homer");
Simpson bart = new Simpson(1, "Homer");
boolean isHashcodeEquals = homer.hashCode() == bart.hashCode();
if (isHashcodeEquals) {
System.out.println("Should compare with equals method too.");
} else {
System.out.println("Should not compare with equals method because " +
"the id is different, that means the objects are not equals for sure.");
}
}
static class Simpson {
int id;
String name;
public Simpson(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Simpson simpson = (Simpson) o;
return id == simpson.id &&
name.equals(simpson.name);
}
@Override
public int hashCode() {
return id;
}
}
}
중복허용을 하지 않는 Set의 경우
- HashSet
- TreeSet
- LinkedHashSet
- CopyOnWriteArraySet
- Set은 오직 Unique한 요소만 insert 할 수 있기 때문에 예를 들어 HashSet에 element를 넣고 싶다면
equals()와 hashcode()를 반드시 가장 먼저 사용해서 element가 unique 한지 판단하게 된다.
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
🔥 접두사가 “Hash” 인 coolection 을 사용하는 경우
예를 들어 HashSet, HashMap, Hashtable, LinkedHashMap인 경우
기능이 정상적으로 작동하게 하려면
hashcode()와 equals() 매서드를 재정의 해줘야 합니다.
같은 unique한 hashcode ID를 갖고 있는 경우 equals() 메서드를 사용하고
hashcode ID가 다른 경우 equals() 메서드를 사용하지 않는 것이 좋다.
만약 hashcode() 결과 값이 | equals() 사용 여부 |
---|---|
return true | equals() 사용 한다 |
return false | equals() 사용하지 않는 것이 좋음 |
객체 비교 시
hashcode() 비교를 할 때 false를 리턴한다면 equals() 메서드는 반드시 false를 리턴해야 한다.
왜냐면 hashcode가 다르다면 확실히 objects는 같지 않기 때문이다.
hashcode() return “true” | equals() method should return |
---|---|
true | true or false |
false | false |
equals() 메서드가 true로 반환될 시 모든 값과 속성에서 동일함을 의미하고
이 경우 hashcode()도 return true 여야 한다.
equals() 매서드 반환값 | hashcode() method should return |
---|---|
true | true |
false | true or false |
public class EqualsHashCodeChallenge {
public static void main(String... doYourBest) {
System.out.println(new Simpson("Bart").equals(new Simpson("Bart")));
Simpson overriddenHomer = new Simpson("Homer") {
public int hashCode() {
return (43 + 777) + 1;
}
};
System.out.println(new Simpson("Homer").equals(overriddenHomer));
Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge")));
set.add(new Simpson("Homer"));
set.add(overriddenHomer);
System.out.println(set.size());
}
static class Simpson {
String name;
Simpson(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
Simpson otherSimpson = (Simpson) obj;
return this.name.equals(otherSimpson.name) &&
this.hashCode() == otherSimpson.hashCode();
}
@Override
public int hashCode() {
return (43 + 777);
}
}
}
A) True, True, 4
B) True, False, 3
C) True, False, 2
D) False, True, 3
System.out.println(new Simpson("Bart").equals(new Simpson("Bart")));
System.out.println(new Simpson("Homer").equals(overriddenHomer));
Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge")));
set.add(new Simpson("Homer"));
set.add(overriddenHomer);
답 : B
https://www.infoworld.com/article/3305792/comparing-java-objects-with-equals-and-hashcode.html
https://mangkyu.tistory.com/101
이펙티브자바