Java collection series - HashSet

Original article, reprint please mark the source: Java collection series - HashSet

I. overview

HashSet is a set set set based on Hash implementation. In fact, its underlying layer is a value fixed HashMap.
HashMap is unordered, so HashSet is also unordered, and HashSet allows null values, but only one null value, that is, the same elements are not allowed to be stored.

2. Constant variable

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    //...
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    //...
}

The above map is the bottom HashMap of the HashSet. For the operation of the HashSet, all operations are transferred to this map.
The above PRESENT is the fixed value of the value of the key value pair in the underlying HashMap. Should only focus on key in HashSet.

III. constructor

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable{
    //...
    public HashSet() {
        map = new HashMap<>();
    }
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }    
    //...
}

Obviously, all the constructors of the HashSet end up creating the underlying HashMap.
The last constructor creates a linked HashMap instance, which is also a HashMap. It inherits from the HashMap and is a function extension collection of the HashMap. It supports multiple order traversal (insertion order and access order).

Four, operation

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable{
    //...
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    public int size() {
        return map.size();
    }
    public boolean isEmpty() {
        return map.isEmpty();
    }
    public boolean contains(Object o) {
        return map.containsKey(o);
    }
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    public void clear() {
        map.clear();
    }
    //...
}

All the basic operations above are completed by the corresponding methods of the opened HashMap.

V. serialization

5.1 serialization

When a HashSet instance is serialized, the map attribute is not serialized because it is decorated with the transient keyword. Reference source code:

// This writeObject method will be executed during the serialization operation
public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    //...
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        // Used to write non static and non transient field values in an object to the stream
        s.defaultWriteObject();
        // Write out HashMap capacity and load factor
        // Writes the current capacity and load factor of the underlying HashMap to the stream
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());
        // Write out size
        // Write the current element number size of the underlying HashMap to the stream
        s.writeInt(map.size());
        // Write out all elements in the proper order.
        // Finally, all elements are written to the stream
        for (E e : map.keySet())
            s.writeObject(e);
    }
    //...
}

5.2 deserialization

// This readObject method will be executed during deserialization
public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    //...
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        // Read the non static and non transient values of the corresponding current class in the stream
        s.defaultReadObject();
        // Read capacity and verify non-negative.
        // Read the capacity value in the stream
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }
        // Read load factor and verify positive and non NaN.
        // Read load factor value in stream
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }
        // Read size and verify non-negative.
        // Read the value of the number of elements in the stream
        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
        // Create the underlying HashMap instance
        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.
        // Read the saved elements in the stream and add them one by one to the newly created HashMap instance
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }
    //...
}

Six, summary

HashSet is implemented by HashMap.

Tags: Java Attribute

Posted on Mon, 02 Dec 2019 09:31:20 -0500 by nonexist