๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๋นˆ ๊ตฌ๋ฉ ์ฑ„์šฐ๊ธฐ

[Java] ์™œ HashSet ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜๋Š” HashMap์€ transient ์˜ˆ์•ฝ์–ด๋ฅผ ์จ์„œ ์„ ์–ธ๋์„๊นŒ

์ถœ์ฒ˜

ChatGPT

์ž๋ฐ” 1.8 ์†Œ์Šค ์ฝ”๋“œ


์ž๋ฐ” 1.8์˜ HashSet ๊ตฌํ˜„ ์ค‘ ์ฝ”๋“œ

HashSet์€ ๋‚ด๋ถ€์— HashMap์„ ์ด์šฉํ•˜๊ณ , ์ด HashMap์€ transient ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

private transient HashMap<E,Object> map;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

public HashSet() {
    map = new HashMap<>();
}

public boolean add(E e) {
    return map.put(e, PRESENT) == null;
}

 

 

์ด์œ 

HashSet ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ HashMap์ด transient ์˜ˆ์•ฝ์–ด๋กœ ์„ ์–ธ๋œ ์ด์œ ๋Š” HashSet์ด ์ง๋ ฌํ™”๋  ๋•Œ ํŠน์ •ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ง๋ ฌํ™” ๊ณผ์ •์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.

 

1. ์ง๋ ฌํ™” ํ˜•์‹ ์ œ์–ด

  • HashSet ํด๋ž˜์Šค๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์šฐํ•ด HashMap์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, HashSet์˜ ์ง๋ ฌํ™” ํ˜•์‹์€ HshMap์˜ ๊ตฌ์กฐ์™€ ๋‹ค๋ฅด๋‹ค. HashSet์€ ๋‹จ์ˆœํžˆ ์ง‘ํ•ฉ(set)์œผ๋กœ์„œ์˜ ๊ธฐ๋Šฅ๋งŒ์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ง๋ ฌํ™” ์‹œ์—๋„ HashMap ์ „์ฒด๋ฅผ ์ง๋ ฌํ™”ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋Œ€์‹ , HashSet์— ์ €์žฅ๋œ ์š”์†Œ๋“ค๋งŒ ์ง๋ ฌํ™”ํ•˜๋ฉด ๋œ๋‹ค.
  • HashMap์„ transient๋กœ ์„ ์–ธํ•จ์œผ๋กœ์จ, ๊ธฐ๋ณธ ์ง๋ ฌํ™” ๋ฐฉ์‹์—์„œ๋Š” HashMap ์ž์ฒด๊ฐ€ ์ง๋ ฌํ™”๋˜์ง€ ์•Š๋„๋ก ํ•˜๊ณ , HashSet์˜ ์ง๋ ฌํ™” ๋งค์ปค๋‹ˆ์ฆ˜์„ ํ†ตํ•ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ„๋กœ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

2. ์ปค์Šคํ…€ ์ง๋ ฌํ™” ๊ตฌํ˜„

  • HashSet ํด๋ž˜์Šค๋Š” wirteObject์™€ readObject ๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ด ์ปค์Šคํ…€ ์ง๋ ฌํ™” ๋ฐ ์—ญ์ง๋ ฌํ™” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“ค์€ HashSet์— ํฌํ•จ๋œ ์š”์†Œ๋“ค๋งŒ ์ง๋ ฌํ™” ๋ฐ ์—ญ์ง๋ ฌํ™”ํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ, HashMap ํ•„๋“œ๊ฐ€ transient๋กœ ์„ ์–ธ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, JVM์€ ์ด ํ•„๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๊ณ , ๋Œ€์‹  HashSet ํด๋ž˜์Šค์˜ ์ปค์Šคํ…€ ๋กœ์ง์— ๋”ฐ๋ผ ์š”์†Œ๋“ค์ด ์ง๋ ฌํ™”๋œ๋‹ค.

HashSet์˜ ์ปค์Šคํ…€ ์ง๋ ฌํ™” ๊ตฌํ˜„์ด ๋œ ์†Œ์Šค ์ฝ”๋“œ๋Š” ์•„๋ž˜์— ์žˆ๋‹ค.

3. ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์˜ ์ง๋ ฌํ™” ๋ฐฉ์ง€

๋งŒ์•ฝ HashMap์ด ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ ๊ทธ๋Œ€๋กœ ์ง๋ ฌํ™”๋œ๋‹ค๋ฉด, HashSet์— ํ•„์š”ํ•œ ๊ฒƒ ์ด์ƒ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์ง๋ ฌํ™”๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ๊ณ , ์ง๋ ฌํ™”๋œ ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์ ธ ๋„คํŠธ์›Œํฌ ์ „์†ก ์‹œ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค.

 

์ฝ”๋“œ

์ž๋ฐ” 1.8์˜ HashSet ํด๋ž˜์Šค์˜ ์ง๋ ฌํ™” ๊ด€๋ จ ๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋‹ค.

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    //Set์˜ ์š”์†Œ๋“ค์„ ์ง๋ ฌํ™”ํ•˜๋Š” ๋กœ์ง
    
    // Write out any hidden serialization magic
    s.defaultWriteObject();
    // Write out HashMap capacity and load factor
    s.writeInt(map.capacity());
    s.writeFloat(map.loadFactor());
    // Write out size
    s.writeInt(map.size());
    // Write out all elements in the proper order.
    for (E e : map.keySet())
        s.writeObject(e);
}

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Set์˜ ์š”์†Œ๋“ค์„ ์—ญ์ง๋ ฌํ™”ํ•˜๋Š” ๋กœ์ง
    
    // Read in any hidden serialization magic
    s.defaultReadObject();
    // Read capacity and verify non-negative.
    int capacity = s.readInt();
    if (capacity < 0) {
        throw new InvalidObjectException("Illegal capacity: " +
                                         capacity);
    }
    // Read load factor and verify positive and non NaN.
    float loadFactor = s.readFloat();
    if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
        throw new InvalidObjectException("Illegal load factor: " +
                                         loadFactor);
    }
    // Read size and verify non-negative.
    int size = s.readInt();
    if (size < 0) {
        throw new InvalidObjectException("Illegal size: " +
                                         size);
    }
    // Set the capacity according to the size and load factor ensuring that
    // the HashMap is at least 25% full but clamping to maximum capacity.
    capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
            HashMap.MAXIMUM_CAPACITY);
    // Constructing the backing map will lazily create an array when the first element is
    // added, so check it before construction. Call HashMap.tableSizeFor to compute the
    // actual allocation size. Check Map.Entry[].class since it's the nearest public type to
    // what is actually created.
    SharedSecrets.getJavaOISAccess()
                 .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));
    // Create backing HashMap
    map = (((HashSet<?>)this) instanceof LinkedHashSet ?
           new LinkedHashMap<E,Object>(capacity, loadFactor) :
           new HashMap<E,Object>(capacity, loadFactor));
    // Read in all elements in the proper order.
    for (int i=0; i<size; i++) {
        @SuppressWarnings("unchecked")
            E e = (E) s.readObject();
        map.put(e, PRESENT);
    }
}

 

transient ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด HashMap์„ ์ง๋ ฌํ™” ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•˜๊ณ , ๋Œ€์‹  HashSet์˜ ์š”์†Œ๋“ค๋งŒ ์ง๋ ฌํ™”ํ•ด ํšจ์œจ์„ฑ์„ ๋†’์ธ ๊ฒƒ์ด๋‹ค.

 

๊ฒฐ๋ก 

HashSet ๋‚ด๋ถ€์—์„œ HashMap ํ•„๋“œ๋ฅผ transient๋กœ ์„ ์–ธํ•œ ๊ฒƒ์€ ์ง๋ ฌํ™” ์‹œ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ”ผํ•˜๊ณ , HashSet์— ์ ํ•ฉํ•œ ์ง๋ ฌํ™” ํ˜•์‹์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์กฐ์น˜์ด๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๊ณ  ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.