자바를 공부하던 중 hashCode와 equals 함수는 왜 계속 상속을 받을까라는 의문부터 시작하였다.
일단 해시라는 놈부터 알아보자
해시란 key와 value(실제 데이터의 값)가 한 쌍으로 존재하며 key값이 배열의 인덱스(정수)로 변환되는 데이터를 다루는 기법 중 하나이다.
key로 value를 알 수 있다는 말이되고 검색과 저장이 아주 빠르게 이루어지게 된다.
시간 복잡도는 O(1)에 수렴하는 것으로 보아 아주 좋은 기법이라고 볼 수 있다.
위에서 말한 해시를 만들어주는 함수로 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다.
출처: <파이썬 알고리즘 인터뷰>
위의 사진을 보면 윤아와 서현이 같은 해시를 사용하게 되는 것을 볼 수 있다.
이것을 해시 충돌이라고 하는데 그래서 java에서는 equals() 함수가 단짝으로 붙어 다니게 된다.
충돌이 나는 이유는 인덱스를 한정된 인덱스로 바꾸게 된다면 다른 해시코드여도 같은 인덱스가 나올 수 있는 것이다.
(결국 알고리즘을 잘 써야된다)
String a = newe String("euqals");
String b = newe String("euqals");
System.out.println(a.equals(b)); // -- true
System.out.println(a == b); // -- false
위 코드를 보자
a와 b는 같은 값의 문자열이다. 하지만 각자의 new 생성자로 다른 메모리에 할당될 것이다.
그리고 위에서 equals는 두 객체가 같은지 확인을 한다고 했다.
메모리 할당이 다른 곳으로 되어있으니 false가 나와야되지 않은가? 했지만
true가 나왔다. 이유는 String 클래스에서 equals 함수를 오버라이드하여 객체가 같은 값을 같는지 확인했기 때문이다.
그 이유는 간단하다. 프로그래밍 상에는 같은 값으로 인식하기 위해서이다.
(그러니 같은 객체로 인식하도록 하는 것이다)

Object.equals의 일반적인 계약 외에도 레코드 클래스는 레코드 컴포넌트 접근자 메서드의 결과를 정식 생성자에 전달하여 레코드 인스턴스가 "복사"될 때 다음과 같이 불변성을 따라야 합니다
레코드 인스턴스가 복사될 때 따라간다고 적혀있는 것을 볼 수 있다.
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2)); //false
Set<Employee> employees = new HashSet<Employee>();
employees.add(e1);
employees.add(e2);
}
}
당연히 생성자를 이용해 객체를 생성하여 equals로 했을 때 false가 나올 것이다.
그럼 Employee class안에서 equals를 오버라이딩한다면 위에서 말했던 것처럼 같은 값으로 인식하게 도와줄 것이다.
public boolean equals(Object o) {
if(o == null) {
return false;
}
if (o == this) {
return true;
}
if (getClass() != o.getClass()) {
return false;
}
Employee e = (Employee) o;
return (this.getId() == e.getId());
}
(lombok을 이용하면 자동으로 오버라이딩이 가능하다)
하지만 여전히 HashSet에 대한 처리를 고려해야 된다.
hashCode 메서드를 오버라이딩하지 않았다면 동일한 객체로 판단되더라도 각 객체는 다른 해시 코드를 반환하게 된다.
동일한 객체임에도 불구하고 다른 해시 코드를 가지게 되면 이러한 객체들은 해시 테이블에 저장될테고 이는 메모리 공간의 낭비로 이어져 성능에 영향을 미칠 수 있다.
따라서 equals 메서드와 함께 hashCode 메서드도 함께 오버라이딩하여 짝을 이루어야 한다.
hashCode와 equals를 오버라이드 하는 메소드 내부에서 Getter를 사용하기를 권장하고 있다.
그 이유는 ORM에 의해 fields가 Lazy Loaded되어 getter를 부르기 전에는 사용이 불가능할 수 있기 때문이다.
예를 들어 만약 Employee 클래스의 정보가 Lazy loaded 되었다면 id에 0이 할당되어 e1.id == e2.id가 0==0으로 처리될 수 있기 때문이다. 하지만 이것을 e1.getId() == e2.getId()로 수정한다면 ORM에 의해 id에 값이 할당된 후에 getId()가 호출가능하므로, 오작동을 멈출 수 있다.
참고:
https://howtodoinjava.com/java/basics/java-hashcode-equals-methods/
https://siyoon210.tistory.com/85
https://devlog-wjdrbs96.tistory.com/243