본문 바로가기

JAVA

Effective java 정복기 2장

728x90

아이템_10 equals는 일반 규악을 지켜 재정의해라

 

 equals는 언제 재정의 할까?

 

equals는 논리적인 동치성을 확인하고 싶을 때 재정의 한다.

Enum: 값 클래스라고 해도 값이 같은 인스턴스가 둘 이상 만들어지지 않음을 보장할때

 

hashCode: 객체의 주소값을 변환하여 생성한 객체의 고유한 정수값

 

equals 메서드:

  • 목적: 두 객체의 "내용"이 같은지를 비교합니다.
  • 기본 동작:
    • 기본적으로는 Object 클래스의 equals 메서드를 상속받아, 두 객체의 참조값(주소)을 비교합니다.
    • 하지만 필요에 따라 **객체의 내용(필드 값)**을 비교하도록 equals를 재정의(override)할 수 있습니다.
  • 사용 예:
    • 문자열 비교: String 클래스는 equals를 재정의하여 문자열의 내용을 비교하도록 구현되어 있습니다.
String s1 = new String("test"); 
String s2 = new String("test"); 
System.out.println(s1.equals(s2)); // true (내용이 같으므로 true)

hashCode 메서드:

  • 목적: 객체를 해시 기반 컬렉션(예: HashMap, HashSet)에서 효율적으로 사용할 수 있도록 해시 값을 제공합니다.
  • 기본 동작:
    • Object 클래스의 기본 구현은 객체의 메모리 주소를 기반으로 해시 값을 계산합니다.
    • 필요하면, 객체의 상태(필드 값)를 기반으로 해시 값을 계산하도록 hashCode를 재정의할 수 있습니다.
  • 사용 예:
    • 동일한 hashCode를 가지는 객체는 동일한 버킷에 저장되며, 충돌이 발생할 경우 equals를 사용해 추가 비교를 수행합니다.

중요한 관계:

  • 재정의할 때의 규칙:
    1. equals가 true인 두 객체는 반드시 **동일한 hashCode**를 반환해야 합니다.
      • 그렇지 않으면 해시 기반 컬렉션에서 비정상적으로 동작합니다.
    2. 하지만 hashCode가 같다고 해서 equals가 반드시 true는 아닙니다.
      • 해시 충돌(다른 객체가 동일한 해시 값을 가지는 경우)은 가능하기 때문입니다.
  • 예시:
     
     
class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

결론:

  • equals: 객체의 "내용"이 같은지 확인하는 메서드입니다.
  • hashCode: 객체를 해시 기반 데이터 구조에서 사용할 때, 효율적인 검색을 위해 고유한 정수값을 반환합니다.
  • Object 클래스의 기본 hashCode는 객체의 메모리 주소를 기반으로 해시 값을 계산합니다.
  • 하지만 객체의 내용을 기반으로 해시 값을 계산해야 할 경우 hashCode를 재정의(override) 해야 합니다.

 

아이템_14 Comparable을 구현할지 고려하라

 

 

 

Comparable 인터페이스에는 compareTo(T o) 메소드 하나가 선언되어있는 것을 볼 수 있다. 이 말은 우리가 만약 Comparable을 사용하고자 한다면 compareTo 메소드를 재정의(Override/구현)을 해주어야 한다는 것이다

 

Comparator를 보면 선언 된 메소드가 많아서 어질할 수 있겠지만, 우리가 실질적으로 구현해야 하는 것은 단 하나다.

바로 compare(T o1, T o2) 다.

 

 

이 말은, default나 static으로 선언된 메소드가 아니면 이는 추상메소드라는 의미로 반드시 재정의를 해주어야 한다는 것이다.

 

 

(참고로 bool equals(Object obj) 메소드는 default나 static이 안붙어있음에도 구현이 강제되지 않는 이유는 모든 객체의 최상위 타입(객체)인 Object 클래스에서 정의되어있기 때문이다.)

 

 

Comparable은 자기 자신과 파라미터로 들어오는 객체를 비교하는 것이고, Comparator는 자기 자신의 상태가 어떻던 상관없이 파라미터로 들어오는 두 객체를 비교하는 것이다. 즉, 본질적으로 비교한다는 것 자체는 같지만, 비교 대상이 다르다는 것이다.

 

 Comparable은 lang패키지에 있기 때문에 import 를 해줄 필요가 없지만, Comparator는 util패키지에 있다.

 

 

 

 

 

 

728x90

'JAVA' 카테고리의 다른 글

ParallelStream은 무엇일까?  (0) 2024.12.01
스프링 첫요청이 처리되는데 오래 걸리는 이유  (0) 2024.11.25