본문 바로가기
개발공부/JAVA

[JAVA] equals() 와 hashCode()

by 양히◡̈ 2022. 9. 21.

quals()

equals가 구현된 방법은 2개의 객체가 참조하는 것이 동일한지(동일성 비교)를 확인하는 것이다.

즉, 2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체가 된다.

public boolean equals(Object obj) {
    return (this == obj);
}

따라서, 두 객체가 타입과 속성이 같으면 같다고 비교하고 싶으면, equals()를 재정의(override)해서 동등성 비교를 하면 된다.

 

참고로, String 객체에서 equals() 메소드를 호출하면 자연스럽게 동등성 비교가 되는데, 이는 String Class가 equals()를 재정의하고 있기 때문이다.

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String aString = (String)anObject;
            if (!COMPACT_STRINGS || this.coder == aString.coder) {
                return StringLatin1.equals(value, aString.value);
            }
        }
        return false;
    }

 

 

hashCode()

int hashCode()로 정의된 hashCode 메소드는 실행 중에(Runtime) 객체의 유일한 integer값을 반환한다. Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환하도록 되어있다. (항상 그런 것은 아니다.)

hashCode는 HashTable과 같은 자료구조를 사용할 때 데이터가 저장되는 위치를 결정하기 위해 사용된다.

 

 

 

equals()와 hashCode()의 관계

동일한 객체는 동일한 메모리 주소를 갖는다는 것을 의미하므로, 동일한 객체는 동일한 해시코드를 가져야 한다. 그렇기 때문에 만약 우리가 equals() 메소드를 오버라이드 한다면, hashCode() 메소드도 오버라이드 되어야 한다.

  • Java 프로그램을 실행하는 동안 equals에 사용된 정보가 수정되지 않았다면, hashCode는 항상 동일한 정수값을 반환해야 한다. (Java의 프로그램을 실행할 때 마다 달라지는 것은 상관이 없다.)
  • 두 객체가 equals()에 의해 동일하다면, 두 객체의 hashCode() 값도 일치해야 한다.
  • 두 객체가 equals()에 의해 동일하지 않다면, 두 객체의 hashCode() 값은 일치하지 않아도 된다.

equals()와 hashCode()를 같이 재정의하지 않으면 hash를 사용한 collection(HashSet, HashMap, HashTable)을 사용할 때 문제될 수 있다.

 

 

* Reference : https://mangkyu.tistory.com/101

 

[Java] equals와 hashCode 함수

1. equals와 hashCode란? equals와 hashCode는 모든 Java 객체의 부모 객체인 Object 클래스에 정의되어 있다. 그렇기 때문에 Java의 모든 객체는 Object 클래스에 정의된 equals와 hashCode 함수를 상속받고 있다..

mangkyu.tistory.com

 

 


 

hashCode() 재정의시 주의점

@Override
public int hashCode() {
  return 11;
}

이처럼 모든 객체에 대해 똑같은 해시코드를 반환하면 모든 객체가 같은 해시테이블 버킷에 담겨 Linked List처럼 동작하게 된다.
평균 수행시간이 O(1)에서 O(n)으로 느려져서, 성능이 매우 낮아질 뿐더러 버킷에 대한 overflow가 발생하는 경우 데이터가 누락될 수도 있다.

따라서, 좋은 해시 함수를 만들기 위해서는 서로 다른 인스턴스에 대해 각기 다른 해시코드를 반환할 수 있어야된다.

 

 

 

좋은 해시 함수 만들기

@Override
public int hashCode() {
    int c = 31;
    //1. int변수 result를 선언한 후 첫번째 핵심 필드에 대한 hashcode로 초기화 한다.
    int result = Integer.hashCode(firstNumber);

    //2. 기본타입 필드라면 Type.hashCode()를 실행한다
    //Type은 기본타입의 Boxing 클래스이다.
    result = c * result + Integer.hashCode(secondNumber);

    //3. 참조타입이라면 참조타입에 대한 hashcode 함수를 호출 한다.
    //4. 값이 null이면 0을 더해 준다.
    result = c * result + address == null ? 0 : address.hashCode();

    //5. 필드가 배열이라면 핵심 원소를 각각 필드처럼 다룬다.
    for (String elem : arr) {
      result = c * result + elem == null ? 0 : elem.hashCode();
    }

    //6. 배열의 모든 원소가 핵심필드이면 Arrays.hashCode를 이용한다.
    result = c * result + Arrays.hashCode(arr);

    //7. result = 31 * result + c 형태로 초기화 하여 result를 리턴한다.
    return result;
}
  • 곱할 숫자를 31로 지정하는 이유는 31이 홀수 이면서 소수(prime)이기 때문이다. 2를 곱하는 것은 shift연산과 같은 결과를 내기 떄문에 추천하지 않는다.
  • 31을 이용하면 (i << 5) - i와 같이 최적화 할 수 있다.

 

* Reference : https://jaehun2841.github.io/2019/01/12/effective-java-item11/

 

Item 11. Equals를 재정의하려거든 Hashcode도 재정의하라 | Carrey`s 기술블로그

서론 equals를 재정의한 클래스에서는 hashcode도 재정의 해야한다. 그렇지 않으면 hash를 사용하는 HashMap, HashSet과 같은 컬렉션의 원소로 사용될 때 문제가 발생할 것이다. 이번 글을 읽기 전에 아래

jaehun2841.github.io

 

'개발공부 > JAVA' 카테고리의 다른 글

Thread  (0) 2023.05.24
[JAVA] ArrayList 내부 구현  (0) 2022.09.26
[JAVA] System.out.println()을 쓰면 안 되는 이유  (1) 2022.09.26
[JAVA] String, StringBuilder, StringBuffer  (1) 2022.09.22
[JAVA] 메모리 구조(Stack/Heap)  (2) 2022.09.21

댓글