티스토리 뷰
자바를 사용하면서 HashMap은 빼놓을 수 없는 컬렉션 중 하나이다.
HashMap은 Map 인터페이스를 구현한 컬렉션으로 Key, Value를 가진다.
Key와 Value가 짝을 이루어 입력되고 후에 찾을 때 get 메서드에 Key를 전달함으로서 Value 값을 얻을 수 있다.
아래는 String, Integer를 Key, Value로 갖는 예제이다.
public class HashMapTest {
public static HashMap<String, Integer> map;
public static void init() {
map.put("a", 1);
map.put("b", 2);
}
public static void main(String[] args) {
map = new HashMap<>();
init();
String test = "a";
System.out.println("##: " + map.get(test));
}
}
결과:
##: 1
("a", 1)과 ("b", 2)가 들어있는 HashMap에서 "a"에 대한 Value값을 get 메서드를 통해 가져오는 예제이다.
"a"를 get에 넣으니 정상적으로 1이 나왔다.
그렇다면 Key에 우리가 만든 클래스를 사용한다면 어떤 결과가 나올까
public class HashMapTest {
public static HashMap<Foo, Integer> map;
public static void init() {
map.put(new Foo("a"), 1);
map.put(new Foo("b"), 2);
}
public static void main(String[] args) {
map = new HashMap<>();
init();
Foo c = new Foo("b");
System.out.println("##: " + map.get(c));
}
static class Foo{
String key;
public Foo(String key) {
this.key = key;
}
}
}
위 경우 우리가 기대하는 결과 값은
##: 2
일 것이다.
하지만 결과는
##: null
이 나오게 된다.
이유는 간단하다.
우리가 만든 클래스에 hashCode와 equals를 Override하지 않아 발생한 문제이다.
위 String이나 Integer에서는 해당 문제가 발생하지 않는 이유도 String, Integer 등 클래스에는 이미 hashCode와 equals를 Override하고 있기 때문이다.
우선 HashMap의 put 메서드를 보자
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
입력받은 key에 대해 hash메서드를 호출한다.
HashMap내 hash메서드는 아래와 같다.
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
key에 대한 hashCode를 호출하게 된다. 즉, 직접 만든 class내에 hashCode 메서드를 Override하지 않으면 Object의 hashCode 메서드를 호출하게 된다.
이런 경우 다음과 같은 결과가 나온다.
public class HashCodeTest {
public static void main(String[] args) {
System.out.println("##: " + new Foo("a").hashCode());
System.out.println("##: " + new Foo("b").hashCode());
System.out.println("##: " + new Foo("b").hashCode());
}
static class Foo{
String key;
public Foo(String key) {
this.key = key;
}
}
}
결과:
##: 32374789
##: 1973538135
##: 1023487453
즉, 기본 hashCode 메서드를 호출하게 되면 Foo 클래스 내 key가 같아도 다른 hash값이 나오게 된다.
그리고 HashMap의 get 메서드를 보자
public V get(Object key) {
Node<K,V> e;
return (e = getNode(key)) == null ? null : e.value;
}
getNode는 아래와 같다.
final Node<K,V> getNode(Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & (hash = hash(key))]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
간단하게 보면 key에 대한 hash값을 구한 뒤 hash값이 같고 equals 메서드를 통해 비교한 결과가 true인 경우 해당 값을 반환하도록 되어있다.
그러기 때문에 커스텀 클래스를 만든 경우 hashCode와 equals를 오버라이드해주지 않으면 HashMap에서 정상적으로 동작하지 않게 된다.
꼭, hashCode와 equals를 오버라이드 하자!
마지막으로 hashCode와 equals를 오버라이드 한 후 HashMap을 사용한 결과이다.
public class HashMapTest {
public static HashMap<Foo, Integer> map;
public static void init() {
map.put(new Foo("a"), 1);
map.put(new Foo("b"), 2);
}
public static void main(String[] args) {
map = new HashMap<>();
init();
Foo c = new Foo("c");
c.key = "b";
System.out.println("##: " + map.get(c));
}
static class Foo{
String key;
public Foo(String key) {
this.key = key;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null || getClass() != obj.getClass()) return false;
Foo t = (Foo) obj;
return this.key.equals(t.key);
}
}
}
결과:
##: 2
- Total
- Today
- Yesterday
- 어노테이션
- 스타트업
- 트랜젝션
- ioc container
- MQ
- JPA
- 싱글톤 패턴
- Singleton
- 해시맵
- 의존성 주입
- 디자인패턴
- di
- Get
- Tistory
- 자바
- 외주개발
- 디자인 패턴
- IOC
- Kafka
- docker
- 스프링
- 의존성
- Spring
- dependency injection
- 카프카
- IoC 컨테이너
- @Transactional
- 업체선정
- 싱글톤
- HashMap
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |