DevAndy
145
2021-05-14 20:25:51
6
1031

Java equals()와 hashCode() 정리


공부하면서 정리해보았는데, 선배 개발자분들의 피드백 부탁드리겠습니다!


항상 다른 분들 쓴거 구경만 하다가 처음 블로그에 정리한걸 공유하네요..ㅎㅎ


혹시 제가 잘못이해했거나 추가 학습해야할게 있다면 댓글로 피드백 주시면 감사하겠습니다..!


https://youngjinmo.github.io/2021/05/equals-hashcode/

1
  • 댓글 6

  • 개발자2
    191
    2021-05-15 02:09:42 작성 2021-05-15 03:03:27 수정됨

    잘 보았습니다. 면접가면 자주 물어보는 질문이네요 ㅎ

    링크에서 마지막 부분

    여기서 중요한건 equals()만 오버라이딩해서는 안되고, hashCode()까지 오버라이딩해야 정상적으로 동작한다는 것이다.

    에서 Set 에서 equals(), hashCode() 둘다 오버라이딩 해야 add 기능이 동작하는 이유는 뭘까요?

    중요하다고 한것은 Set 의 경우에만 국한된 건가요?

    Set 에서 말고 hashCode 의 용도는 또 무엇일까요? 

  • 개발자2
    191
    2021-05-15 03:02:48

    Set은 중복을 허용하지 않는다. 따라서 똑같은 속성을 지닌 Card 객체를 HashSet에 삽입했다면, HashSet에선 중복되는 데이터 하나는 삭제하고, size()로 1을 반환했어야 했다.


    위에서 데이터 하나는 삭제한다라는 것은 잘못된 정보입니다. 아래는 HashSet 의 add 의 내용입니다. 중복되는 데이터 하나는 삭제하는것은 아니고 the call leaves the set unchanged and returns false 라고 되어 있습니다.

    변동없이 그대로 놔두고 false 리턴한다가 맞습니다.

    https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html#add-E-

    public boolean add(E e)
    Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.
    Specified by:add in interface Collection<E>Specified by:add in interface Set<E>Overrides:add in class AbstractCollection<E>Parameters:e - element to be added to this setReturns:true if this set did not already contain the specified element


  • charny
    105
    2021-05-15 13:50:23
    @개발자2

    댓글에서도 배우고 갑니다. 감사합니다 :)
  • DevAndy
    145
    2021-05-15 18:21:53

    @개발자2 피드백 감사합니다!!

    먼저 HashSet에서 add()의 동작원리를 모르고 있었는데, 두번째 댓글로 알려주셔서 공식문서를 통해 HashSet의 add()를 더 공부할 수 있었습니다. 감사합니다.

    equals()와 hashCode()를 함께 오버라이딩 했던 이유는 비교하는 두 객체의 hashCode()가 같아야만 equals()로 true 를 반환받을 수 있을것 같았습니다.

    또 자료구조에서는 Map처럼 Key, Value를 한 쌍으로 저장하는 자료구조로 해시 테이블이라는 개념이 존재하는데 이 해시 테이블에서 hashCode()를 key로서 사용한다는걸 새로 배우게 되었습니다.

    오라클 공식문서에서 Set 인터페이스는 해시 테이블이 지원하는 인터페이스라는 사실을 알게되었습니다.

    > This class implements the `Set` interface, backed by a hash table (actually a `HashMap` instance)

    그래서 HashSet의 add()를 이용할때, equals()와 hashCode()를 모두 재정의해줘야지만 의도한대로 동작할 수 있었다고 생각합니다.

  • 개발자2
    191
    2021-05-16 01:07:39 작성 2021-05-16 13:05:34 수정됨

    네 아래 내용도 보시면 좋을듯 합니다. 오래전 보았던 내용이라 다시 정리해보면서 공유드립니다. :-)


    equals and hashCode contract 의 개념이 있습니다. 

    • 2개의 object 에 대해, equals() 가 true 이면, hashCode() 도 서로 같아야 합니다. 
    • 2개의 object 에 대해, equals() 가 false 이면, hashCode() 가 같을 수도 다를수도 있다.


    간단히 말하면 a.equals(b) 이면 a.hashCode() == b.hashCode() 입니다. 

    위 contract 을 만족시키기 위해 equals() 를 override 했으면 반드시 hashCode() 도 override 해서 구현해야 합니다.

    그렇지 않으면 contract violation 입니다.


    뭐 살짝 contract 안지킬수도 있지 빌드도 잘되고 아무 이상없는데?

    라고 할 수도 있지만, hash 기반 collection 을 사용할 경우는 다시 생각해 봐야 합니다. 내부 구현에서 equals() 와 hashCode() 가 사용되기 때문이죠.


    아래는 Effective Java 의 내용 일부인데, equals() 를 override 했으면, hashCode() 도 override 해야 하는 이유를 설명합니다.


    You must override hashCode in every class that overrides equals. If you fail to do so, your class will violate the general contract for hashCode, which will prevent it from functioning properly in collections such as HashMap and HashSet.


    아래 이미지는 HashMap 의 bucket 과 hashCode(), equals() 의 관계를 나타냅니다.

    java hashmap, how hashmap works in java

    HashMap, HashSet 같은 hash 기반 collection 에서는 데이터 저장시,

    • hashCode() 를 키값으로 bucket 에 저장하고 같은 값이 있을경우(hash collision), 내부적으로 LinkedList 에 넣습니다.


    위의 내용대로라면 hashCode() 를 제대로 구현하지 않으면 hash collision 자주 일어나게되고, 데이터 추가/삭제시 퍼포먼스가 느려지겠죠. 왜냐하면 한번에 키값으로 bucket 에서 값을 가져오지 못하고 bucket → LinkedList 를 거쳐서 가져와야 하니까요.


    알고 있으면 좋은 내용이라서 적어봤습니다. 즐코딩요~


  • DevAndy
    145
    2021-05-16 10:47:56
    안그래도 찾아보다가 좀 헷갈리는 부분이었는데.. 정말 감사합니다!!
    주말 즐겁게 보내세요!!
  • 로그인을 하시면 댓글을 등록할 수 있습니다.