HashMap을 사용할 때, put(key, value)으로 데이터를 저장할 수 있는데 HashMap의 경우 key 값이 존재할 경우 value 값을 대체하고 기존의 value 값을 반환해준다. 하지만 key 값이 존재하지 않는 경우에는 새로 데이터를 저장하고 null을 반환한다. (기존 value 값이 없기 때문)
put()으로 데이터를 저장하고 반환된 value 객체를 참조할려고 하니 NullPointerException이 발생한다는 경고를 보고 어떻게 구현이 되어있나 확인해보았다.
HashMap에서 해싱이란 해시 함수를 이용해서 데이터를 해시테이블에 저장하고 검색하는 방식을 말하는데 해시함수가 데이터가 저장되어 있는 곳을 알려주기 때문에 다량의 데이터 중에서 원하는 데이터를 빠르게 찾을 수 있다.
해싱에서 사용하는 자료구조는 배열과 링크드 리스트의 조합으로 되어 있는데 key 값을 해시함수에 넣으면 배열의 한 요소를 얻게 되고, 다시 그 곳에 연결되어 있는 링크드 리스트에 저장이 된다.
해시함수의 계산 결과인 해시코드로 해당 값이 저장되어 있는 링크드 리스트를 찾고, 링크드 리스트에서 검색한 키와 일치하는 데이터를 찾는다. (링크드 리스트는 검색에 불리하기 때문에 해시코드가 서로 중복되지 않도록 하는 전략이 좋다.)
HashMap의 경우 Object 클래스에 정의된 hashCode()를 해시 함수로 사용하는데 이는 객체의 주소를 이용하는 알고리즘으로 모든 객체에 대해 hashCode() 결과가 서로 유일하게 된다.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
그래서 실제 HashMap의 put() 메서드를 보면 putVal() 메서드를 호출하면서 해시함수 hash(key)를 실행하고 해시 코드를 넘겨준다.
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
...
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
putVal() 메서드의 일부를 보면 key가 존재하는지 확인을 하고 value를 교체한 뒤 oldValue를 반환하거나 그렇지 않을 경우 null을 반환한다.
'Java' 카테고리의 다른 글
[Java] Optional 체크 주의점(orElse, orElseGet) (0) | 2022.09.30 |
---|---|
[Java] 제네릭에서 Raw Type 선언 주의 (이펙티브 자바) (0) | 2022.08.16 |
[Java] equals() 사용시 주의점 (NullPointerException) (0) | 2022.08.04 |
[Java] System.arraycopy() 와 Arrays.copyOf() 차이점 (JMH 벤치마킹) (0) | 2022.08.03 |
[Java] ENUM의 활용 ( (0) | 2022.07.22 |