Java

HashSet/HashMap์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ ๋ฐ ์ค‘๋ณต ์ œ๊ฑฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜

9taetae9 2025. 1. 22. 11:59
728x90

HashSet์€ Java Collections Framework์˜ ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ ์ค‘ ํ•˜๋‚˜๋กœ, ์ค‘๋ณต์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ์ง‘ํ•ฉ(Set) ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

 

1. HashSet์˜ ์ฃผ์š” ํŠน์ง•

  • ์ค‘๋ณต ์š”์†Œ๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ : ๋ฐ์ดํ„ฐ ๋™๋“ฑ์„ฑ ํ™•์ธ์„ ์œ„ํ•œ equals(), hashCode() ๋ฉ”์„œ๋“œ ํ•„์š”
  • ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ : ์ธ๋ฑ์Šค๊ฐ€ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ๋„˜์–ด๊ฐ€๋Š” ๋ฉ”์„œ๋“œ(get(int index), indexOf(Object o) ๋“ฑ)์ด ์—†์Œ
  • null ์š”์†Œ ํ—ˆ์šฉ 
  • ๋น„๋™๊ธฐ(non-synchronized) ๊ตฌํ˜„
  • O(1) ์‹œ๊ฐ„ ๋ณต์žก๋„์˜ ๊ธฐ๋ณธ ์—ฐ์‚ฐ

2. ๋‚ด๋ถ€ ๊ตฌ์กฐ 

2-1) ์ฃผ์š” ํ•„๋“œ

HashSet ํ•„๋“œ

์œ„์˜ ์ฝ”๋“œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด 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 - add() ๋ฉ”์„œ๋“œ

 

HashSet์€ ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ HashMap์˜ put() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

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;
}

 

  1. ํ…Œ์ด๋ธ” ์ดˆ๊ธฐํ™” ๊ฒ€์‚ฌ
    • ํ…Œ์ด๋ธ”์ด ๋น„์–ด์žˆ์œผ๋ฉด resize()๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐํ™”
    • ๊ธฐ๋ณธ ์ดˆ๊ธฐ ํฌ๊ธฐ๋Š” 16
  2. ๋ฒ„ํ‚ท ์œ„์น˜ ๊ณ„์‚ฐ
    • (n-1) & hash ์—ฐ์‚ฐ์œผ๋กœ ์ธ๋ฑ์Šค ๊ณ„์‚ฐ
    • ๋น„์–ด์žˆ๋Š” ๋ฒ„ํ‚ท์ด๋ฉด ์ƒˆ ๋…ธ๋“œ ์ง์ ‘ ์‚ฝ์ž…
  3. ํ‚ค ์ค‘๋ณต ๊ฒ€์‚ฌ
    • ํ•ด์‹œ๊ฐ’ ๋น„๊ต (์ฒซ ํ•„ํ„ฐ๋ง)
    • ๋ ˆํผ๋Ÿฐ์Šค ๋™์ผ์„ฑ ๊ฒ€์‚ฌ
    • equals() ๋ฉ”์„œ๋“œ๋กœ ๋‚ด์šฉ ๋น„๊ต
  4. ํŠธ๋ฆฌ ๋…ธ๋“œ ์ฒ˜๋ฆฌ
    • Red-Black ํŠธ๋ฆฌ ๊ตฌ์กฐ ํ™œ์šฉ
    • ๋กœ๊ทธ ์‹œ๊ฐ„ ๋ณต์žก๋„ ๋ณด์žฅ
  5. ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ์ฒ˜๋ฆฌ
    • ์ˆœ์ฐจ ํƒ์ƒ‰์œผ๋กœ ์ค‘๋ณต ๊ฒ€์‚ฌ
    • ํ•„์š”์‹œ ํŠธ๋ฆฌ๋กœ ๋ณ€ํ™˜

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 ์Œ
๋น„๊ต์  ๋А๋ฆผ ์„ฑ๋Šฅ ๋น ๋ฆ„

 

728x90