Frudy
3k
2019-06-27 12:30:31 작성 2019-06-27 12:36:58 수정됨
8
1299

hashCode(), equals(), 주소값에 대한 이야기


무려 1년 3개월이 걸린 고민이 드디어 오늘 싹 내려가네요. 역시 개발은 즐거워요.

[정리된 내용] 보다는, [고민순서]로 글을 작성해볼게요.


1. 주소값 vs hashCode값은 같은가?

>> 다릅니다.

>> An object's address is (probably) unobtainable, and certainly useless. [스택오버플로우 링크]


C와달리 자바에서는, 객체의 주소를 얻을 수도없고, 쓸모도 없을거라고 이야기합니다.

어떤식으로든 hashCode와 주소값은 관련이 전혀 네버 없다고 합니다.



C언어부터 배웠던 저는, 주소값에 집착을 했었습니다.

그래서 1년 3개월내내 hashCode == 주소값인줄 알았구요.



2. hashCode() vs 주소값

주소값 = 메모리상에 객체가 저장된 위치일 뿐이였어요.

hashCode = 객체를 구분하기위한 정수값일 뿐이에요.


(1) 왜 객체를 구분하려고 하는지?

(2) 왜 정수값인지? 에 대한 고민이 생겼고,

hashCode의 필요성을 검색해본결과


그냥 해쉬맵, 해쉬테이블에 저장하기위해서 였습니다.

--> 왜 해쉬맵, 해쉬테이블에 저장하려고 하는대요?

--> 이 자료구조는 빠르니까요. 평균적인 시간복잡도가 매우 작다고해요. https://siyoon210.tistory.com/85


정리)

빠른 자료구조인 해쉬맵, 해쉬테이블이란게 있는대, 

여기서 객체들을 구분하기위해 hashCode라는걸 사용하므로,

개발자는 객체간에 구분이 잘 되도록, (= 충돌없이) key값을 정수형으로 잘 변환해야한다.

이에 대해 방법론으로 해시알고리즘이란것도 있었으나 저는 모르겠습니다.


3. equals() vs hashCode() 구글링 자료들

equals = 두 객체의 내용이 같은지

hashCode = 두 객체가 같은 객체인지


저 말 때문에 햇갈렸었습니다. "같다"의 정의가 안나와있었으니까요.

다시 생각하지만, 주소값 vs 해쉬값은 아무런 연관이 없습니다.


equals()가 무슨메소드인지는 다들 아실거같아 생략할게요.


4. equals(), hashCode() 작성하는 방법

데이터를 저장할 클래스를 설계할 때, equals()와 hashCode()메소드를 어떻게 오버라이딩 해야하는가?

이런 고민이 생겼었습니다.


예를들어 Dto클래스를 설계한다면 이런식으로 자료구조에 저장하고,

이후에 삭제하거나 가져올 일이 앞으로 많기 때문입니다.


그래서, String, List, SimpleDateFormat 등등

Java에서 제공하는 클래스들은 어떻게 equals()와 hashCode()를 오버라이딩했는지

원본소스를 까보기 시작했습니다.


>> 이것은 개발자의 재량이며 동시에 역량이었습니다.


어떻게 구현하건간에 아무튼 결론은 의도에 맞게 잘 구현만 하면 됬습니다.

그러므로, equals()와 hashCode() 작성 가이드도 찾을 수 있었습니다.


https://www.geeksforgeeks.org/equals-hashcode-methods-java/


그리고 저 가이드에 맞게 구현된 원본 소스는 다음과 같습니다.


public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }


String클래스의 equals()의 원본소스입니다.


1. equals() 목적 : 두 객체의 내용이 같은지 비교한다 (O) 아주 잘 구현했네요.

2. equals() 가이드

(1) 참조값 a와 b에 대해 a.equals (b) 와 b.equals(a)는 동일한 결과를... (O) 아주 잘 구현했네요.

(이하 생략)


그리고 hashCode()소스들도 모두 까본결과,

그냥 의도와 가이드에 맞게 작성만 하면 됬었습니다. 

(제가 해시 알고리즘에 대해 모르니까 구현을 당장 해본다면 막 충돌이 날 뿐입니다)



5. hashCode() 가 필요한 이유

>> 저같은 초보개발자에게 가장 중요한 파트에요.


(1) hashCode()를 오버라이딩 해야하는 이유

Set : 데이터의 중복을 허용하지 않음. 이라고 알고계시죠?


어떻게 Set은 중복제거를 구현했을까? 싶어서 찾아봤어요.


(HashSet클래스)

    public boolean add(E e) {

        return map.put(e, PRESENT)==null;

    } 이었고,


    private transient HashMap<E,Object> map;

    private static final Object PRESENT = new Object(); 이었어요.


내부적으로 해쉬맵을 쓰고있었고,

HashMap은 key의 hashCode가 같으면 덮어쓰기 때문에 

이런 방법으로 중복을 제거하고 있었더라구요.


그래서 hashCode()를 오버라이딩하지 않으면, Set의 저 기능을 이용할 수 없어요.


BoardDto클래스 hashCode() 오버라이딩안했고,

동일한 객체 (boardDto)를 넣으면 중복이 제거되지만..


저렇게 하면 중복이 제거되지않습니다.


물론 hashCode()를 오버라이딩하면 중복이 잘 제거됩니다.


6. equals() 가 필요한 이유

equals()메소드 오버라이딩을 안하면요,

List인터페이스를 상속하는 클래스의 메소드들의 올바른 동작을 보장할 수 없어요.

(아마 다른 콜렉션도 그럴거같은대 모르겠네요 리스트 주로 써서..)


(1) ArrayList클래스의 equals() (두 리스트가 같은지) 못쓰구요, https://okky.kr/article/588001

(2) 같은이유로 remove(E e)도 못쓰구요,

기타 못쓰는 메소드가 많았어요.


이런것들을 사용하려면 equals()를 오버라이딩 해야해요.


잘못된 내용 있다면 감사히 지적받겠습니다.


앞으로 수학공부도 해야하나봐요..

hashCode() 오버라이딩하려면 해시알고리즘 알아봐야하니까요

5
2
  • 댓글 8

  • Frudy
    3k
    2019-06-27 13:38:53

    어... 사는얘기에 올린걸로 확인했는데

    게시판이 변경되어있네요..;

    0
  • ignoreOrange
    1k
    2019-06-27 14:48:45

    잘읽었습니다.

    0
  • Frudy
    3k
    2019-06-27 14:52:18
    혹시 잘못된 내용이 있나요??
    0
  • LichKing
    14k
    2019-06-27 23:14:32

    잘써주셨네요.

    조금 덧붙이면 hashcode는 얼마든지 서로 다른 내용의 객체끼리 충돌이 발생할수있기때문에 Set과같은 자료구조가 hashcode만 가지고 작동하지는 않습니다. hashcode와 equals를 모두 확인합니다.

    때문에 hashcode가 동일한 두 객체가 equals를 false를 리턴한다면 Set은 이를 다른 객체로 관리하고 중복제거하지않습니다.

    때문에 hashcode는 같아도 equals는 false인 경우가 존재할 수 있습니다.

    다만 equals는 true인데 hashcode가 다를순 없습니다.

    이런 규칙이 있기때문에 항상 equals와 hashcode는 쌍으로 오버라이딩해서 사용하게됩니다.

    0
  • rvo
    102
    2019-06-28 02:40:18
    드문경우이나 hash 함수를 만들 때 k != k지만 hash(k) = hash(k) 인 경우가 생기면 심각한 보안 위협이므로 hash함수를 만들 때 randomness를 쓰기도 합니다.
    0
  • Frudy
    3k
    2019-06-28 03:17:20
    헉 equals 오버라이딩 없이
    hashCode만 오버라이딩해도
    잘 제거되기에 착각을 했습니다.

    어디서 어떻게 쓰이는지
    또 찾아봐야겠군요.

    잘못된 내용 짚어주셔서 감사합니다.
    0
  • 개발가락
    124
    2019-07-02 12:51:28

    제2의 창천향로님이 되실 것 같은 느낌이 드네요. 혹시 블로그도 하시나요~??

    0
  • Frudy
    3k
    2019-07-02 14:47:15

    milkpaper

    아녀, 아직 초보라... 괜히 잘못된내용 퍼나를거같아서 안해요.

    0
  • 로그인을 하시면 댓글을 등록할 수 있습니다.