HashSet/HashMap์ ๋ด๋ถ ๊ตฌ์กฐ ๋ฐ ์ค๋ณต ์ ๊ฑฐ ๋ฉ์ปค๋์ฆ
HashSet์ Java Collections Framework์ ํต์ฌ ๊ตฌ์ฑ ์์ ์ค ํ๋๋ก, ์ค๋ณต์ ํ์ฉํ์ง ์๋ ์งํฉ(Set) ์๋ฃ๊ตฌ์กฐ๋ฅผ ๊ตฌํํ๋ค.
1. HashSet์ ์ฃผ์ ํน์ง
- ์ค๋ณต ์์๋ฅผ ํ์ฉํ์ง ์์ : ๋ฐ์ดํฐ ๋๋ฑ์ฑ ํ์ธ์ ์ํ equals(), hashCode() ๋ฉ์๋ ํ์
- ์์๋ฅผ ๋ณด์ฅํ์ง ์์ : ์ธ๋ฑ์ค๊ฐ ๋งค๊ฐ ๋ณ์๋ก ๋์ด๊ฐ๋ ๋ฉ์๋(get(int index), indexOf(Object o) ๋ฑ)์ด ์์
- null ์์ ํ์ฉ
- ๋น๋๊ธฐ(non-synchronized) ๊ตฌํ
- O(1) ์๊ฐ ๋ณต์ก๋์ ๊ธฐ๋ณธ ์ฐ์ฐ
2. ๋ด๋ถ ๊ตฌ์กฐ
2-1) ์ฃผ์ ํ๋
์์ ์ฝ๋์์ ๋ณผ ์ ์๋ฏ์ด HashSet์ ๋ด๋ถ์ ์ผ๋ก HashMap์ ์ฌ์ฉํ์ฌ ๊ตฌํํ๋ค.
HashSet์ ์์๋ค์ HashMap์ key๋ก ์ ์ฅํ๋ค.
๋ชจ๋ ๊ฐ์ ๋ํ value๋ก๋ ๋์ผํ ๋๋ฏธ ๊ฐ์ฒด(PRESENT)๋ฅผ ์ฌ์ฉํ๋ค. (ํค์ ์กด์ฌ ์ฌ๋ถ๋ง ํ์ธํ๋ฉด ๋๊ธฐ ๋๋ฌธ)
2-2) ์์ฑ์
1. HashSet() : ๊ธฐ๋ณธ ์์ฑ์, ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์๋ 16๊ฐ์ ๊ณต๊ฐ๊ณผ 0.75์ ๋ก๋ ํฉํฐ๋ฅผ ๊ฐ๋ ๊ฐ์ฒด ์์ฑ
2. HashSet(Collection<? extends E> c) : ๋งค๊ฐ ๋ณ์๋ก ๋ฐ์ ์ปฌ๋์ ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฅผ HashSet์ ์ ์ฅ
3. HashSet(int initialCapacity) : ๋งค๊ฐ ๋ณ์๋ก ๋ฐ์ ๊ฐ์๋งํผ์ ๋ฐ์ดํฐ ์ ์ฅ ๊ณต๊ฐ๊ณผ 0.75์ ๋ก๋ ํฉํฐ๋ฅผ ๊ฐ๋ ๊ฐ์ฒด ์์ฑ
4. HashSet(int initialCapacity, float loadFactor) : ์ฒซ ๋งค๊ฐ ๋ณ์๋ก ๋ฐ์ ๊ฐ์๋งํผ์ ๋ฐ์ดํฐ ์ ์ฅ ๊ณต๊ฐ๊ณผ ๋ ๋ฒ์งธ ๋งค๊ฐ ๋ณ์๋ก ๋ฐ์ ๋งํผ์ ๋ก๋ ํฉํฐ๋ฅผ ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ์์ฑ
2-3) ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ
HashMap ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ๊ตฌ์ฑ๋๋ค.
- ๋ฒํท ๋ฐฐ์ด (Node<K,V>[])
- ๊ฐ ๋ฒํท์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋๋ ํธ๋ฆฌ ๊ตฌ์กฐ
- ๋ฉํ๋ฐ์ดํฐ (ํฌ๊ธฐ, ์์ ํ์ ๋ฑ)
3. HashSet์ ์ค๋ณต ์ ๊ฑฐ ๋ฐฉ์
3-1) ๋์ ๋ฐฉ์
HashSet์ ์ค๋ณต ์ ๊ฑฐ ๋ฐฉ์์ ์์๋ณด๊ธฐ ์ํด add() ๋ฉ์๋๋ฅผ ๋ณด์.
HashSet์ ์๋ก์ด ์์๋ฅผ ์ถ๊ฐํ ๋ HashMap์ put() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
put()์ ๋ง์ฝ ๋์ผํ ํค๊ฐ ์ด๋ฏธ ์กด์ฌํ๋ค๋ฉด ํค์ ๋์ํ๋ previous value๋ฅผ ๋ฆฌํดํ๊ณ , ํค๊ฐ ์กด์ฌํ์ง ์์ผ๋ฉด null์ ๋ฆฌํดํ๋ค.
HashSet์์๋ ๋ชจ๋ value๊ฐ PRESENT๋ก ๋์ผํ๊ธฐ ๋๋ฌธ์, map.put()์ด PRESENT๋ฅผ ๋ฆฌํดํ๋ฉด ๋์ผํ key๊ฐ ์กด์ฌํ๋ ๊ฒ์ด๋ค.
3-2) ํด์
1. ๊ฐ์ฒด์ hashCode() ํธ์ถ
2. ํด์ ๋ณด์์ฒ๋ฆฌ
3. ๋ฒํท ์ธ๋ฑ์ค ๊ณ์ฐ
index = (n - 1) & hash
- n์ ๋ฒํท ๋ฐฐ์ด์ ํฌ๊ธฐ (ํญ์ 2์ ๊ฑฐ๋ญ์ ๊ณฑ)
- hash๋ ๊ณ์ฐ๋ ํด์๊ฐ
3-3) putVal() ๋์
HashSet์ add() ๋ฉ์๋๋ map.put()์ ํธ์ถํ๊ณ ๋ด๋ถ์ ์ผ๋ก HashMap์ putVal() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 1. ํ
์ด๋ธ ์ด๊ธฐํ ๊ฒ์ฌ
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 2. ๋ฒํท ์์น ๊ณ์ฐ ๋ฐ ๋น ๋ฒํท ์ฒ๋ฆฌ
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
// 3. ํค ์ค๋ณต ๊ฒ์ฌ
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 4. ํธ๋ฆฌ ๋
ธ๋ ์ฒ๋ฆฌ
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
// 5. ์ฐ๊ฒฐ ๋ฆฌ์คํธ ์ฒ๋ฆฌ
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;
}
}
// 6. ๊ธฐ์กด ๊ฐ ๊ฐฑ์
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
// 7. ํฌ๊ธฐ ๊ด๋ฆฌ
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
- ํ
์ด๋ธ ์ด๊ธฐํ ๊ฒ์ฌ
- ํ ์ด๋ธ์ด ๋น์ด์์ผ๋ฉด resize()๋ฅผ ํตํด ์ด๊ธฐํ
- ๊ธฐ๋ณธ ์ด๊ธฐ ํฌ๊ธฐ๋ 16
- ๋ฒํท ์์น ๊ณ์ฐ
- (n-1) & hash ์ฐ์ฐ์ผ๋ก ์ธ๋ฑ์ค ๊ณ์ฐ
- ๋น์ด์๋ ๋ฒํท์ด๋ฉด ์ ๋ ธ๋ ์ง์ ์ฝ์
- ํค ์ค๋ณต ๊ฒ์ฌ
- ํด์๊ฐ ๋น๊ต (์ฒซ ํํฐ๋ง)
- ๋ ํผ๋ฐ์ค ๋์ผ์ฑ ๊ฒ์ฌ
- equals() ๋ฉ์๋๋ก ๋ด์ฉ ๋น๊ต
- ํธ๋ฆฌ ๋
ธ๋ ์ฒ๋ฆฌ
- Red-Black ํธ๋ฆฌ ๊ตฌ์กฐ ํ์ฉ
- ๋ก๊ทธ ์๊ฐ ๋ณต์ก๋ ๋ณด์ฅ
- ์ฐ๊ฒฐ ๋ฆฌ์คํธ ์ฒ๋ฆฌ
- ์์ฐจ ํ์์ผ๋ก ์ค๋ณต ๊ฒ์ฌ
- ํ์์ ํธ๋ฆฌ๋ก ๋ณํ
4. ์ถฉ๋ ํด๊ฒฐ ๋ฐฉ์
4-1) ์ฐ๊ฒฐ ๋ฆฌ์คํธ(Separate Chaining)
- ๋์ผํ ๋ฒํท์ ์ฌ๋ฌ ๋ ธ๋๋ฅผ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ์ ์ฅ
- ๊ฐ๋จํ์ง๋ง ๊ธธ์ด๊ฐ ๊ธธ์ด์ง ์ ์๋ ๋จ์
4-2 ํธ๋ฆฌํ(Tree Conversion)
๋ฒํท์ ํฌ๊ธฐ๊ฐ ํน์ ์๊ณ๊ฐ์ ๋์ผ๋ฉด Red-Black ํธ๋ฆฌ๋ก ๋ณํ
- TREEIFY_THRESHOLD = 8 (ํธ๋ฆฌํ ์๊ณ๊ฐ)
- UNTREEIFY_THRESHOLD = 6 (์ญํธ๋ฆฌํ ์๊ณ๊ฐ)
- ์ต์ ๋ฒํท ๋ฐฐ์ด ํฌ๊ธฐ = 64
5. ์ฑ๋ฅ ์ต์ ํ
5-1) ๋ก๋ ํฉํฐ(Load Factor)
static final float DEFAULT_LOAD_FACTOR = 0.75f;
- ๊ณต๊ฐ๊ณผ ์๊ฐ์ ํธ๋ ์ด๋์คํ
- ๋ฎ์ ๊ฐ: ๋น ๋ฅธ ๊ฒ์, ๋ง์ ๋ฉ๋ชจ๋ฆฌ
- ๋์ ๊ฐ: ์ ์ ๋ฉ๋ชจ๋ฆฌ, ๋๋ฆฐ ๊ฒ์
5-2) ๋ฆฌ์ฌ์ด์ง(Resizing)
final Node<K,V>[] resize() {
// ์ ํฌ๊ธฐ ๊ณ์ฐ
// ๋ชจ๋ ์์ ์ฌ๋ฐฐ์น
// ์๊ณ๊ฐ ์กฐ์
}
์ฃผ์ ๋ณ์
Node<K,V>[] oldTab = table; // ํ์ฌ ํ
์ด๋ธ
int oldCap = (oldTab == null) ? 0 : oldTab.length; // ํ์ฌ ํ
์ด๋ธ ํฌ๊ธฐ
int oldThr = threshold; // ํ์ฌ ์๊ณ๊ฐ
int newCap, newThr = 0; // ์๋ก์ด ํฌ๊ธฐ์ ์๊ณ๊ฐ
1. ์๋ก์ด ํฌ๊ธฐ ๊ฒฐ์
1-1) ๊ธฐ์กด ํ ์ด๋ธ์ด ์๋ ๊ฒฝ์ฐ (oldCap > 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; // ํฌ๊ธฐ์ ์๊ณ๊ฐ์ 2๋ฐฐ๋ก
}
1-2) ์ด๊ธฐ ์์ฑ ์ (oldCap == 0)
else if (oldThr > 0) // ์ด๊ธฐ ์ฉ๋์ด ์๊ณ๊ฐ์ ์ง์ ๋ ๊ฒฝ์ฐ
newCap = oldThr;
else { // ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
2. ๋ฐ์ดํฐ ์ฌ๋ฐฐ์น
if (e.next == null) // ๋จ์ผ ๋
ธ๋ ์ฒ๋ฆฌ
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof HashMap.TreeNode) //ํธ๋ฆฌ ๋
ธ๋ ์ฒ๋ฆฌ
((HashMap.TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // ๋งํฌ๋ ๋ฆฌ์คํธ ์ฒ๋ฆฌ
HashMap.Node<K,V> loHead = null, loTail = null; //๊ธฐ์กด ์ธ๋ฑ์ค
HashMap.Node<K,V> hiHead = null, hiTail = null; //์ ์ธ๋ฑ์ค
HashMap.Node<K,V> next;
do { ... //๋
ธ๋ ๋ถํ ๋ก์ง
6. HashSet, HashMap ๋น๊ต
HashSet | HashMap | |
Set ์ธํฐํ์ด์ค ๊ตฌํ | ๊ตฌํ | Map ์ธํฐํ์ด์ค ๊ตฌํ |
๊ฐ์ฒด(Objects) | ์ ์ฅ ๋์ | key - value ์ |
๋น๊ต์ ๋๋ฆผ | ์ฑ๋ฅ | ๋น ๋ฆ |