다르다.

몰랐다.

그래서

한참을

돌았다.

그런데.

똑같네?

다른줄

알았다.


이게 무슨 소리지..

처음엔 내가 잘못 알고 있나? 싶었다. 


어떤 숫자가 있는지 없는지만 확인하면 되는 로직에 HashSet<Integer>를 사용했다.

거기에 0부터 24까지의 정수를 추가했다고 하면,

위와 같은 코드가 될 것이다.


그리고 저 아래와 같은 코드를 실행하면 출력 결과가 true인 것은 자명하다.


System.out.println(set.contains(1));


그렇다면 1 대신에 1l 또는 (long)1을 넣으면 true일 것이라고 예상했다.

헌데 결과는 false 이다.

내가 아는 바로는 int 의 표현 범위 내에서 int 와 long은 같은 hashCode를 가진다.

그래서 확인을 해봤다.


System.out.println(Integer.hashCode(1));

System.out.println(Long.hashCode(1));


실행 결과는 생각한대로 둘 다 1이다. int 범위 내의 어떤 값이든 그렇다.

그리고 정수 기본타입은 자신의 값이 곧 자신의 해시코드라고 알고 있었는데,

HashSet 이란 놈이 이렇게 배신을 할 줄이야.

이름만 보면 Set에 포함된 엘리먼트들의 유일성을 Hash 값으로 판단한단 말이 아닌가?


HashSet의 코드를 뜯어보니 HashSet은 내부적으로 엘리먼트의 저장을 HashMap을 사용한다.

왜인지는 모르겠다. 코드의 중복을 줄이기 위해서인지? 구현 편의인지.

HashMap은 Entry를 사용하니까 엘리먼트 하나를 저장하는 것보다 키, 밸류를 저장하는 게 손해아닌가..?

HashSet에서는 HashMap의 Entry의 value 값에 빈 Object객체를 넣는다.

빈 Object 객체는 static 변수로 선언되어 있다.

(private static final Object PRESENT = new Object();)


어쨌거나 어디서부터 잘못 이해하고 있는지 확인 하기 위해서 HashSet에서 contains 메서드가 호출하는 HashMap.containsKey 메서드를 살펴봤다.

containsKey에서는 map의 get메서드가 호출하는 getEntry(key)를 호출하고, key를 이용해서 Entry를 가져오지 못하면 null일 때 containsKey는 false가 되고 있으면 true가 된다.

그럼 getEntry에서는 무엇을 하느냐? key의 해시값을 이용해서 HashMap이 가지고 있는 hashSeed값과 적절한 연산을 통해 해시 값을 만들고, 이 해시를 이용해서 해시 테이블에 있는 엔트리를 찾는다.


결국 hash값으로 찾는게 맞단 소리고, HashMap의 hash 메서드의 인자를 long과 int 타입으로 테스트해보니 값이 같을 때 같은 값이 나온다.


여기까지의 결론으로는 결국 set.contains(1) 이나, set.contains((long)1) 이나 같은 결과를 보여야 하는게 정상인데..

왜 false라는 값이 나올까..?? map과 set을 좀 더 파헤쳐봐야겠다.



검색 중 Why you should not user HashSet/HashMap for Integer number 글 발견..

관련있는 글

+ Recent posts