[Java] hashCode와 equals으로 보는 오브젝트의 동등성

민지·2024년 3월 14일
0

Java

목록 보기
8/9
post-thumbnail

오늘의 질문

  • hashCode()equals() 가 무엇인가요?
  • map에서 객체를 키로 사용하면 어떻게 되나요?
  • ==equals()의 차이는 무엇인가요?

면접에서 value와 reference 에 대해서 많은 질문을 받았다.
객체는 참조형이다! 왜 오브젝트가 중요한 지 다시금 되새겼다.

Java의 Object

동등성과 equals()

equals() 메서드는 Object 클래스에서 상속받는다.
이때 equals()는 두 객체가 같은 메모리 주소를 가리키고 있는지를 확인해서 동등성을 비교한다.

  • 같은 메모리 주소다? true 반환
  • 다른 메모리 주소다? false 반환

결국 value 가 같아도 참조값이 다르다면 다르다고 반환한다.

그치만 우리는 대부분 value가 같다면 같은 객체라고 보고 싶을 수 있지요?
따라서 equals()를 재정의한다.

value값이 같다면 동등한 객체로 보고 싶을 때 equals()를 재정의한다.

hashCode()

  • 객체의 해시 코드(정수)를 반환합니다.
  • 해시 기반 컬렉션에서는 이 해시 코드를 사용하여 객체를 저장하거나 검색하는 버킷(bucket)을 결정합니다.
  • 동등한 객체는 반드시 같은 해시 코드를 반환해야 합니다.

따라서 우리가 equals()를 재정의해서 두 객체가 동일합니다~ 라고 판단할 때는 반드시 같은 해시코드를 반환하도록 해야 한다.

안지키면?

해시 기반의 컬렉션들(HashMap, HashSet ...)에서 정상적으로 동작하지 않을 수 있다.

결론적으로 equals()를 재정의할 때에는 hashCode()도 함께 재정의해야 한다.

예시 코드: value가 같은 Object 동등성 비교

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = 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);
    }
}
  • Person 같은 값의 인스턴스 두 개를 비교하면 같다는 결과 출력
  • hashCode를 오버라이딩 해주었으므로 두 해시코드도 같다.
public static void main(String[] args) {
	Person p1 = new Person("aa", 10);
	Person p2 = new Person("aa", 10);
	System.out.println(p1.equals(p2));    // true
    System.out.println(p1.hashCode());    // 97195
    System.out.println(p2.hashCode());    // 97195
}

만약 equals()만 오버라이딩을 해준다면?

두 값은 동등하다고 true를 출력하겠지만, 둘의 해시코드는 달라진다.
이러면 위에서 말했듯 해시를 구현할 때 정확한 값 비교가 안될 수가 있다.

Java의 Map

key-value 로 구성된 자료구조. 고유한 키 값을 가진다.

해시 기반의 컬렉션을 사용할 때 객체를 키로 사용하게 되면 객체의 hashCode()와 equals() 메소드의 구현에 따라 맵의 동작이 크게 달라질 수 있다.

고유한 키 값

그렇다면 고유해야하는 이 키 값이 객체일 경우 어떤 경우를 고유하다고 판단하는가?

동등하다고 판단되면 = equals()true 를 반환하면,
Map에서 같은 키로 간주한다.

예시 코드

위와 같은 Person 객체가 있을 때,

public static void main(String[] args) {
	Map<Person, String> map = new HashMap<>();
	map.put(new Person("John", 30), "Developer");

	System.out.println(map.get(new Person("John", 30))); // Developer
}

새로운 객체를 만들어서 참조값이 다르다고 해도, hashCode가 같아지도록 재정의했기 때문에 결과는 Developer라고 출력하게 된다.

연산자 ==

  • 같은 메모리 주소를 가지고 있는 객체에 한해서 true 를 반환
  • 값이 동등해도 false가 나올 수가 있다
  • 객체의 동등성을 비교할 때에는 equals()를 쓰자.

오늘의 답변

Object의 동등성과 관련된 hashCode()와 equals()에 대해 말씀드리겠습니다.
equals()는 객체의 동등성을 비교하는 메서드로, 기본적으로는 객체 주소값이 동일해야 true를 반환합니다.
hashCode()는 java에서 해시 컬렉션에서 객체를 저장하거나 검색할 때 해시코드를 사용하는데, 이 때의 해시코드를 반환하는 메서드입니다.
값이 같은 경우는 같은 객체라고 보고 싶다면, equals()를 재정의를 해줍니다. 이 때 hashCode()도 같이 재정의를 해주어서 같은 해시코드를 반환하도록 해야합니다. 이를 어기면 해시 컬렉션을 사용할 때 제대로 된 결과값이 나오지 않을 수 있습니다.

Map에서의 키는 동등한 객체일 때, 즉 equals()가 true로 반환되면 같은 키라고 여깁니다. 따라서 적절하게 재정의를 해주어야 합니다.

연산자 ==참조값이 같으면 같은 객체라고 판단합니다. 따라서 기본타입을 비교할 때 사용하는 것을 권장합니다. 객체를 비교할 때에는 equals() 를 사용하여 정확한 동등성을 비교할 수 있도록 합니다.

*Pitty의 추가 질문

  • equals()와 hashCode()를 재정의할 때 고려해야 할 사항은 무엇인가요?

  • hashCode() 메소드를 재정의하는 경우, 어떤 기준으로 해시코드를 생성하는 것이 좋을까요?

    • 객체의 고유하고 변경 불가능한 소성을 사용
    • 계산 비용 최소화
    • 해시 충돌 줄이기 위해 소수 활용
    • 그치만 java에는 Objects.hash() 를 통해 알아서 생성해준다.
  • equals() 메소드를 재정의할 때 instanceof 대신 getClass()를 사용하는 이유는 무엇인가요?

    • 더 정확한 타입 일치를 보장하기 위함이다.

*Pitty는 나의 GPT 프롬프트의 애칭이다.

profile
개발의, 개발에 의한, 개발을 위한 기록장

0개의 댓글

관련 채용 정보