@EqualsAndHashCode 그리고 HashCode와 Equals

jyleever·2023년 1월 8일

애노테이션

목록 보기
1/1

업무하면서 @EqualsAndHashCode 롬복을 자주 봤다. 스프링 프로젝트로 진행하면서 다양한 애노테이션을 사용하는 만큼 각 롬복이 무슨 역할을 하고 어느 상황에서 사용하는지 정확히 알아봐야 한다.

먼저 HashCode에 대해 간단하게 알아보자.

HashCode

  • 해싱 알고리즘에 의해 반환된 정수 값
    HashMap과 같은 자료 구조에서 사용되는 Hash Table에서의 hashing을 가능하게 하는 것을 목적으로 하고 있다.
  • equals 비교에 사용되는 정보가 변경되지 않았다면, 어플리케이션이 실행되는 동안 그 객체의 hashCode 메소드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다.(단, 애플리케이션을 다시 실행한다면 이 값이 달라져도 상관없다.)
  • equals(Object)가 두 객체를 같다고 판단했다면, hashCode는 똑같은 값을 반환해야 한다.
  • equals(Object)가 두 객체를 다르다고 판단했더라도, hashCode가 서로 다른 값을 반환할 필요는 없다. 단, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다.

그렇다면, HashCode는 어떤 원리로 구현되는 걸까? https://www.baeldung.com/java-hashcode#standard-hashcode-implementations 에 따르면 표준 구현 방식은 다음과 같다.

@Override
public int hashCode() {
    int hash = 7;
    hash = 31 * hash + (int) id;
    hash = 31 * hash + (name == null ? 0 : name.hashCode());
    hash = 31 * hash + (email == null ? 0 : email.hashCode());
    return hash;
}

물론, 매번 우리가 직접 HashCode를 오버라이딩하여 생성할 필요는 없다. 인텔리제이가 알아서 구현하는 기능도 제공하고, hash 메소드를 통해서도 생성할 수 있다.

위 HashCode 생성 방식에서 알 수 있는 사실은, HashCode는 객체의 주소값이 아니라, 내부 요소와 값을 다루며 그 값에 따라 HashCode 값도 바뀐다는 것이다.

Equals

String.equals는 가지고 있는 값이 동일하면 TRUE
String == 연산자는 주소값을 비교

  • 하지만, Object 클래스에서의 equals는 equality is the same as object identity, 즉. 객체 동일성이다.

따라서 Object.equals() 메소드는 보통 하위 클래스에서 재정의를 통해 논리적으로 동등한지 비교할 때 주로 사용된다. https://www.baeldung.com/java-equals-method-operator-difference#2-equals-method-with-object-types

public class Person {
    // other fields and methods omitted
    @Override
    public boolean equals(Object o) {
        if (this == o) 
            return true;
        if (o == null || getClass() != o.getClass()) 
            return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
}
@Override
public boolean equals (Object obj) {
	if (obj instanceof Member) {
		Member member = (Member) obj;
		if(id.equals(member.id)) {
			return true;
		}
	}
	return false;
}
  • non-null 객체를 참조할 때 사용하는 등가 관계 구현
    즉, 비교 대상이 되는 객체가 서로 같은 객체인지 판단

HashCode 와 Equals

  • HashCode : 두 객체가 같은 객체인지, 동일성(identity) 를 비교하는 연산자, 두 객체 내부 필드의 값들이 같은지 판단
  • Object.equals() : 두 객체의 내용이 같은지, 동등성(equality) 를 비교하는 연산자

@EqualsAndHashCode

Equality made easy: Generates hashCode and equals implementations from the fields of your object.
클래스 위에 정의하면, 컴파일 시점에 자동으로 객체의 필드로부터 HashCode와 Equals를 오버라이딩하여 구현해주는 애노테이션
클래스에 있는 모든 필드들에 대한 비교를 수행함

  • 일반적으로 모든 non-static, non-transient 필드에서 사용할 수 있다

EqualsAndHashCode의 속성

@EqualsAndHashCode.Include, @EqualsAndHashCode.Exclude

특정 형식의 멤버에 위 속성을 사용하면서 Equals와 HashCode 생성을 위한 비교 수행에서 어떤 필드를 사용할지 수정할 수 있다.

기타 속성

  • onlyExplicitlyIncluded : equals와 hashCode 메소드 자동 생성 시, 정확히 @EqualsAndHashCode.Include가 붙은 메소드나 필드만 포함할지에 대한 설정
    • @EqualsAndHashCode(onlyExplicitlyIncluded = true)
  • CallSuper : equals와 hashCode 메소드 자동 생성 시 부모 클래스의 필드까지 감안할지 안 할지에 대해서 설정
    • callSuper = true : 부모 클래스 필드 값들도 동일한지 체크
      callSuper = false (기본값) : 자신 클래스의 필드 값들만 체크

참고

https://projectlombok.org/features/EqualsAndHashCode
https://www.baeldung.com/java-equals-method-operator-difference
https://www.baeldung.com/java-equals-hashcode-contracts
https://www.baeldung.com/java-hashcode
https://www.daleseo.com/lombok-popular-annotations/
https://junhyunny.github.io/information/java/equals-and-hashcode/
https://angelplayer.tistory.com/134
https://devlog-wjdrbs96.tistory.com/243

0개의 댓글