์ถ์ฒ
ChatGPT
์๋ฐ 1.8 ์์ค ์ฝ๋
์๋ฐ์ HashMap์ ์๋ฐ ์ปฌ๋ ์ ํ๋ ์์ํฌ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ์๋ฃ๊ตฌ์กฐ ์ค ํ๋๋ก, ํค-๊ฐ ์(key-value pair)์ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. HashMap์ ํด์ ํ ์ด๋ธ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ฉฐ, ํค๋ฅผ ํ์ ํจ์๋ก ๋ณํํด ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ์ ์ฅํ๊ณ ๊ฒ์ํ ์ ์๋๋ก ์ค๊ณ๋์๋ค.
1. HashMap์ ์ฃผ์ ํน์ง
์์ ์๊ฐ ์ฑ๋ฅ
์ผ๋ฐ์ ์ผ๋ก HashMap์์ ์ฝ์ , ์ญ์ , ์กฐํ๋ O(1) ์๊ฐ ๋ณต์ก๋๋ฅผ ๊ฐ์ง๋ค. ์ฆ, ๋งค์ฐ ๋น ๋ฅธ ์ ๊ทผ ์ฑ๋ฅ์ ์ ๊ณตํ๋ค. ํ์ง๋ง ํด์ ์ถฉ๋์ด ๋ง์์ง ๊ฒฝ์ฐ, ์ฑ๋ฅ์ O(n)๊น์ง ๋จ์ด์ง ์ ์๋ค.
๋น๋๊ธฐ์
HashMap์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋๊ธฐํ๋์ง ์์ผ๋ฉฐ, ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๋์์ ์์ ๋ ๊ฒฝ์ฐ ์์ ํ์ง ์๋ค. ๋๊ธฐํ๊ฐ ํ์ํ ๊ฒฝ์ฐ, Collections.sysnchronizedMap()์ผ๋ก ๋๊ธฐํ๋ ๋งต์ ์ฌ์ฉํ ์ ์๋ค.
null ํ์ฉ
HashMap์ null ํค์ null ๊ฐ์ ํ์ฉํ๋ค. ๋จ, null ํค๋ ํ๋๋ง ์ ์ฅํ ์ ์์ผ๋ฉฐ, null ๊ฐ์ ์ฌ๋ฌ ๊ฐ ์ ์ฅํ ์ ์๋ค.
2. HashMap์ ๋ด๋ถ ๊ตฌ์กฐ
HashMap์ ๋ด๋ถ์ ์ผ๋ก ๋ฐฐ์ด๊ณผ ์ฐ๊ฒฐ ๋ฆฌ์คํธ(๋๋ ํธ๋ฆฌ ๊ตฌ์กฐ)๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค. ์ด ๋ฐฐ์ด์ ๋ฒํท(bucket)์ด๋ผ๊ณ ํ๋ฉฐ, ๊ฐ ๋ฒํท์๋ ํด์ ์ถฉ๋์ด ๋ฐ์ํ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋๋ ํธ๋ฆฌ๊ฐ ์ ์ฅ๋๋ค.
1. ํด์ ๋ฒํท ๊ตฌ์กฐ
- HashMap์ ํด์ ํจ์๋ฅผ ์ฌ์ฉํด ํค๋ฅผ ํน์ ์ธ๋ฑ์ค๋ก ๋ณํํ๊ณ , ํด๋น ์ธ๋ฑ์ค(๋ฒํท)์ ๊ฐ์ ์ ์ฅํ๋ค.
- ๋์ผํ ํด์ ๊ฐ์ด ๋ฐ์ํ๋ฉด, ํด๋น ๋ฒํท์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ(ํน์ ํธ๋ฆฌ)์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด ์ถฉ๋์ ์ฒ๋ฆฌํ๋ค.
2. Node ํด๋์ค
HashMap์ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฐ ํญ๋ชฉ(ํค-๊ฐ ์)์ Node๋ผ๋ ๋ด๋ถ ํด๋์ค๋ก ํํ๋๋ค. Node๋ ํ๋์ ํค-๊ฐ ์์ ๋ํ๋ด๋ฉฐ, ํด์ ๊ฐ์ ํฌํจํ๊ณ ์๋ค.
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // ํค์ ํด์ ๊ฐ
final K key; // ํค
V value; // ๊ฐ
Node<K,V> next; // ๋ค์ ๋
ธ๋๋ฅผ ๊ฐ๋ฆฌํค๋ ์ฐธ์กฐ(ํด์ ์ถฉ๋ ๋ฐ์ ์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ)
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); }
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
- hash : ํค์ ํด์ ๊ฐ์ผ๋ก, ํด์ ํ ์ด๋ธ์์ ๋ฒํท์ ์ฐพ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
- key : HashMap์ ์ ์ฅ๋ ํค
- value : ํค์ ๋งคํ๋ ๊ฐ
- next : ๋์ผํ ํด์ ๊ฐ์ผ๋ก ์ถฉ๋์ด ๋ฐ์ํ์ ๋, ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ๋ค์ ๋ ธ๋๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
3. ํด์ ํจ์์ ์ถฉ๋ ์ฒ๋ฆฌ
1. ํด์ ํจ์
HashMap์ ํค์ ํด์ ๊ฐ์ ๊ณ์ฐํ๊ธฐ ์ํด hashCode() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค. hashCode()๋ ๊ฐ์ฒด์ ํด์ ์ฝ๋๋ฅผ ๋ฐํํ๋ ๋ฉ์๋๋ก, ์ด ๊ฐ์ HashMap์ด ๋ฐ์ดํฐ๋ฅผ ๋ฒํท์ ์ ์ฅํ ์์น๋ฅผ ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
ํ์ง๋ง hashCode()๋ ๋งค์ฐ ๋ค์ํ 32 ๋นํธ ์ ์ ๊ฐ์ ๋ฐํํ ์ ์์ง๋ง, HashMap์ ์ ํํํ ๋ฐฐ์ด ํฌ๊ธฐ์ ํ๊ณ๊ฐ ์์ผ๋ฏ๋ก, ํด์ ๊ฐ์ ๋ฐฐ์ด์ ํฌ๊ธฐ์ ๋ง๊ฒ ๋นํธ ์ฐ์ฐ์ ์กฐ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋ฒํท ์ธ๋ฑ์ค๋ฅผ ๊ตฌํ๋ค.
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
์ด ๋ฉ์๋์ ํค์ ํด์ ๊ฐ์ ๊ณ์ฐํ ํ, ํด์ ๊ฐ์ ์์ ๋นํธ๋ฅผ ํ์ ๋นํธ์ XOR ์ฐ์ฐํด ํด์ ๊ฐ์ ๋ถํฌ๋ฅผ ๋ ๊ท ์ผํ๊ฒ ๋ง๋ ๋ค.
2. ์ถฉ๋ ์ฒ๋ฆฌ
ํด์ ์ถฉ๋์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ, ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋๋ ํธ๋ฆฌ๋ฅด ์ฌ์ฉํด ์ถฉ๋์ ์ฒ๋ฆฌํ๋ค.
- ์ฐ๊ฒฐ ๋ฆฌ์คํธ : ๊ฐ์ ๋ฒํท์ ์ ์ฅ๋๋ ๋ชจ๋ ํญ๋ชฉ์ Node ๊ฐ์ฒด๋ก ์ฐ๊ฒฐ ๋ฆฌ์คํธ ํํ๋ก ์ ์ฅ๋๋ค.
- ํธ๋ฆฌ : ์๋ฐ 8๋ถํฐ, ์ถฉ๋์ด ๋ง์ด ๋ฐ์ํ ๊ฒฝ์ฐ(๋ฆฌ์คํธ์ ํฌ๊ธฐ๊ฐ 8 ์ด์์ผ ๋), HashMap์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋์ ๋ ๋-๋ธ๋ ํธ๋ฆฌ๋ก ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํด ํ์ ์ฑ๋ฅ์ ํฅ์์ํจ๋ค. ํธ๋ฆฌ๋ก ๋ณํ๋๋ฉด ์๊ฐ ๋ณต์ก๋๋ O(n)์์ O(log n)์ผ๋ก ๊ฐ์ ๋๋ค.
if (binCount >= TREEIFY_THRESHOLD - 1) {
treeifyBin(tab, hash);
}
THEEIFY_THRESHOLD๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก 9์ด๋ค. ๋ฆฌ์คํธ๊ฐ 8๊ฐ ์ด์์ ๋ ธ๋๋ฅผ ๊ฐ์ง๋ฉด ํธ๋ฆฌ๋ก ๋ณํ๋๋ค.
4. HashMap์ ๊ธฐ๋ณธ ๋์
1. ๋ฐ์ดํฐ ์ฝ์ (put ๋ฉ์๋)
๋ฐ์ดํฐ๋ฅผ HashMap์ ์ฝ์ ํ ๋๋ ํค์ ํด์ ๊ฐ์ ๊ณ์ฐํ ํ, ํด๋น ํด์ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ๋ฒํท ์ธ๋ฑ์ค๋ฅผ ์ฐพ๋๋ค. ๋ฒํท์ด ๋น์ด ์์ง ์์ผ๋ฉด, ์ถฉ๋์ด ๋ฐ์ํ ๊ฒ์ด๋ฏ๋ก ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ ํธ๋ฆฌ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ๋ค.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // key already exists
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
- hash(key) : ํค์ ํด์ ๊ฐ์ ๊ณ์ฐํ๋ค.
- resize() : ํ ์ด๋ธ์ด ๊ฝ ์ฐจ๋ฉด ํด์ ํ ์ด๋ธ์ ํฌ๊ธฐ๋ฅผ ๋๋ฆฐ๋ค.
- treeifyBin() : ์ถฉ๋์ด ๋ง์์ง๋ฉด ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ฅผ ํธ๋ฆฌ๋ก ๋ณํํ๋ค.
2. ๋ฐ์ดํฐ ์กฐํ (get ๋ฉ์๋)
๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋๋, ํค์ ํด์ ๊ฐ์ ๊ณ์ฐํ์ฌ ํด๋น ํด์ ๊ฐ์ ๋ง๋ ๋ฒํท์์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋๋ ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ํ์ํ๋ค.
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // check if first node matches
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) { // check if next nodes match
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;
}
ํค์ ํด์ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ํด๋น ๋ฒํท์์ ์์๋ฅผ ์ฐพ๋๋ค. ๋ง์ฝ ์ถฉ๋๋ก์ด ๋ฐ์ํ๋ฉด ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋๋ ํธ๋ฆฌ์์ ํค๋ฅผ ์ฐพ๋๋ค.
5. ๋ฆฌํด์ฑ๊ณผ ์ฑ๋ฅ ์ต์ ํ
HashMap์ ๋ฐ์ดํฐ๊ฐ ๋ง์์ง๋ฉด ์๋์ผ๋ก ํ ์ด๋ธ์ ํฌ๊ธฐ๋ฅผ ๋๋ฆฌ๋๋ฐ, ์ด ๊ณผ์ ์ ๋ฆฌํด์ฑ(rehashing)์ด๋ผ๊ณ ํ๋ค. ํ ์ด๋ธ์ ํฌ๊ธฐ๊ฐ 75%๊ฐ ์ฐจ๋ฉด resize() ๋ฉ์๋๋ฅผ ํตํด ํฌ๊ธฐ๋ฅผ ๋๋ฆฌ๊ณ , ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๋ค์ ํด์ํ์ฌ ์๋ก์ด ํ ์ด๋ธ์ ์ฌ๋ฐฐ์นํ๋ค.
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1;
}
else if (oldThr > 0)
newCap = oldThr;
else {
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else {
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null)
loTail.next = null;
newTab[j] = loHead;
if (hiTail != null)
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
return newTab;
}
๊ธฐ๋ณธ์ ์ผ๋ก ๋ ๋ฐฐ๋ก ํฌ๊ธฐ๋ฅผ ํ์ฅํ๋ฉฐ, ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ํด๋ก์ด ํด์ ํ ์ด๋ธ๋ก ์ฌ๋ฐฐ์นํ๋ค.
6. ๊ฒฐ๋ก
HashMap์ ์๋ฐ์์ ๊ฐ์ฅ ์ค์ํ ์๋ฃ๊ตฌ์กฐ ์ค ํ๋๋ก, ํด์ ํ ์ด๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ๋น ๋ฅธ ์ฑ๋ฅ์ ์ ๊ณตํ๋ค. ํค์ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ํด์ ๋ฒํท์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ , ์ถฉ๋์ด ๋ฐ์ํ๋ฉด ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋๋ ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ์ ์งํ๋ค. HashMap์ ๋ฆฌํด์ฑ์ ํตํด ๋์ ์ผ๋ก ํฌ๊ธฐ๋ฅผ ์กฐ์ ํ๋ฉฐ, ์ฃผ์ด์ง ๋ฐ์ดํฐ์ ๋ํด ๋งค์ฐ ํจ์จ์ ์ผ๋ก ๋์ํ๋ค.
'๋น ๊ตฌ๋ฉ ์ฑ์ฐ๊ธฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Java] ์์ฐ ์์ Natural Ordering (0) | 2024.09.11 |
---|---|
[Java] SortedMap (0) | 2024.09.11 |
[Java] Hashtable์ Enumeration ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค. (0) | 2024.09.11 |
[Java] Fail-fast ๋ฉ์ปค๋์ฆ (0) | 2024.09.11 |
[Java] Map์์๋ Collection View๋ฅผ ์ฌ์ฉํ๋ค. (0) | 2024.09.11 |