jdk8 source code: set subclass

The subclasses inherited from AbstractSet are HashSet, LinkedHashSet, TreeSet, etc.

HashSet

Hold a HashMap reference and use its Entry to save the object (the key field points to the object we actually saved, and the value field points to the same static finalObject object), so the HashSet element cannot be repeated and can have a null. The order of elements is not guaranteed (because HashMap does not guarantee the order of elements, and the table array will automatically expand, and the storage locations of some nodes on the linked list will be moved by the lo and hi algorithms during expansion). The iterator used is the KeyIterator iterator used by the member inner class keySet in HashMap. Not thread safe.

LinkedHashSet

Since it inherits from HashSet (and does not overwrite any methods), it basically inherits all the features of HashSet, but only holds the reference of LinkedHashMap (in addition to storing each linked list in the array, the node also has two fields before and after. The insertion order constitutes a doubly linked list), so the element order is guaranteed. Also because the key of Entry is used to store the object, it is not repeated and can have a null. Not thread safe.

TreeSet

Hold a reference to a TreeMap and use its Entry to save the object (the key field points to the object we actually saved, and the value field points to the same static finalObject object), because the TreeMap object is more dependent on the key's comparator, so we need to save The object cannot be null and must implement the Comparable interface. Since TreeMap does not guarantee the insertion order (because of the rotation and construction of the red-black tree), but guarantees the size order of all elements at a certain moment (the comparator specifies the comparison method), an ordered set of key, value, and Entry can be derived, so TreeSet can Preserves the size order of the elements (sorting specified by the comparator). Not thread safe.

1.HashSet

         HashSet is relatively simple. First, HashSet is inherited from AbstractSet, and the relevant implementation interface source code is as follows:

public class HashSet<E> extends AbstractSet<E>  implements Set<E>, Cloneable, java.io.Serializable
{
}

        All his operations are implemented with the HashMap object, and the object saved to the HashSet is actually the key of the node Entry in the HashMap. So HashSet elements are not repeatable and can be null (null key is in the table[0] position of HashMap). Members declare as follows:

    // save node data
    //key field points to the object we actually saved
    //value field points to the same static immutable object
    //HashSet saves the object and HashMap has the same requirements for saving the key: can not be repeated, can be null
    private transient HashMap<E,Object> map;

    // value of all key-value pairs
    private static final Object PRESENT = new Object();

       His constructor is actually based on several HashMap constructors. It is worth noting that the last constructor will create a LinkedHashMap object instead of a HashMap object. This constructor is for LinkedHashSet. Its source code is as follows:

//constructor
    //The default capacity is 16 and the load factor is 0.75 to create a HashMap
    public HashSet() {
        map = new HashMap<>();
    }
    //Create HashMap with specified capacity
    public HashSet(Collection<? extends E> c) {
    	//Calculate the capacity, this capacity ensures that all elements of the set c are inserted into the table array and will not reach the expansion threshold
        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);
    }

        All operations of the objects that need to be saved are implemented based on the HashMap object. The source code is as follows:

//Since the object we saved is actually the key
    //So directly use the public export method keySet() of HashMap to obtain the Set collection of keys
    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();
    }

        The rest are some Object methods, the source code is as follows:

    //Object method
    @SuppressWarnings("unchecked")
    public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        s.defaultWriteObject();
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());
        s.writeInt(map.size());
        for (E e : map.keySet())
            s.writeObject(e);
    }
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }
    public Spliterator<E> spliterator() {
        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
    }

2.LinkedHashSet

        Compared with LinkedHashSet, it is simpler and inherits from HashMap. The entire class file based on LinkedHashMap is only 40 lines of code. The class declaration source code is as follows:

public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable {
}

        The class file contains only a few constructors, the constructor of the called HashSet, creates a LinkedHashMap object, and the map member inherited from the parent class refers to this newly created object. The source code is as follows:

	//constructor
	//The bottom layer is to call the constructor of HashSet HashSet (initialCapacity, loadFactor, dummy)
	//A LinkedHashMap object will be created
	//The member map inherited from the parent class points to the above object
	public LinkedHashSet(int initialCapacity, float loadFactor) {
		
	    super(initialCapacity, loadFactor, true);
	}
	
	public LinkedHashSet(int initialCapacity) {
	    super(initialCapacity, .75f, true);
	}
	public LinkedHashSet() {
	    super(16, .75f, true);
	}
	public LinkedHashSet(Collection<? extends E> c) {
	    super(Math.max(2*c.size(), 11), .75f, true);
	    addAll(c);
	}
	@Override
	public Spliterator<E> spliterator() {
	    return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
	}

        In addition, it does not cover any parent class methods, but how does it support linked list operations? You can view the call chain of its parent class HashSet method. The source code is as follows:

	//HashSet的add(e)
	public boolean add(E e) {
	    return map.put(e, PRESENT)==null;
	}
	
	//HashMap的put(key,value)
	public V put(K key, V value) {
	    return putVal(hash(key), key, value, false, true);
	}
	//HashMap的putVAl(hash,key,value,onlyIfAbsent,evict)
	final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
		//omit
		//This method records the operation that the node needs to perform after being operated (such as adding to the map)
		afterNodeAccess(e);
		//
		afterNodeInsertion(evict);
		return null;
	}
	//HashMap的afterNodeAccess(e)
	void afterNodeInsertion(boolean evict) {
		//There is no operation in HashMap
	}
	//LinkedHashMap的afterNodeAccess(e)
    void afterNodeAccess(Node<K,V> e) {
    	//omit
        //Here the visited node is added to the tail of the double linked list
    }

        It can be seen that the bottom layer is based on LinkedHashMap to call the method inherited from the parent class. The basic method implementation of the parent class HashMap has taken into account the extension of subsequent subclasses, so using the method for self-use, a function that does not do any processing is called. If the HashMap object calls the basic method, the extension method called inside the method does not do any processing. If the LinkedHashMap object calls the basic method, you can specify how to extend it yourself. Let's look at the iterator implementation. The source code is as follows:

//LinkedHashSet does not override the iterator method of HashSet, the iterator() method of HashSet is as follows:
public Iterator<E> iterator() {
    return map.keySet().iterator();
}
//The keySet() method of LinkedHashMap is as follows:
public Set<K> keySet() {
    Set<K> ks;
    //LinkedKeySet is the inner class implementation of the LinkedHashMap member
    return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
}
//LinkedKeySet member inner class is as follows:
final class LinkedKeySet extends AbstractSet<K> {
    //omit
    public final Iterator<K> iterator() {
    	//LinkedKeyIterator is the inner class implementation of the iterator
        return new LinkedKeyIterator();
    }
}
//LinkedKeyIterator member inner class is as follows:
final class LinkedKeyIterator extends LinkedHashIterator implements Iterator<K> {
		public final K next() {
			//next returns the node pointed to by the after field of the current node, that is, iterates using a double-linked list
			return nextNode().getKey();
			}
}

        We can see that although the method of the parent class is not overridden, the parent class is used to refer to the subclass object, the dynamic binding at runtime is used to call the subclass method, and the inheritance hierarchy and polymorphism are used to conveniently implement all LinkedHashSet. Function.

3.TreeSet

        TreeSet holds a reference to a TreeMap object, and all operations are implemented based on TreeMap. The class file code is just over 200 lines. First look at his interface NavigableSet<E>, which inherits the SortedSet<E> interface to provide more detailed operations related to sorted sets. The source code is as follows:

public interface NavigableSet<E> extends SortedSet<E> {
   
	//returns the largest of all elements in the collection that are smaller than the parameter element
    E lower (E e);
    //returns the largest of all elements in the collection that are less than or equal to the parameter element
    E floor (E e);
    //returns the smallest of all elements in the collection that are greater than or equal to the parameter element
    E ceiling (E e);
    //The return is the smallest of all elements in the collection that are greater than the parameter element E pollLast();
    E higher (E e);
    // Kick the first element out and return
    E pollFirst();
    // Kick the last element out and return
    E pollLast ();
    Iterator<E> iterator();
    //Get an instance of this collection in reverse order
    NavigableSet<E> descendingSet();
    //returns an iterator in reverse order
    Iterator<E> descendingIterator();
    //The method of intercepting this collection, the two boolean parameters indicate whether the intercepted head element and tail element are included.
    NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                           E toElement,   boolean toInclusive);
    // Returns the set of elements before the parameter element, and the boolean parameter indicates whether the parameter element is included.
    NavigableSet<E> headSet(E toElement, boolean inclusive);
    // Returns the set of elements after the parameter element, and the boolean parameter indicates whether the parameter element is included.
    NavigableSet<E> tailSet(E fromElement, boolean inclusive);
    // intercept the collection
    SortedSet<E> subSet(E fromElement, E toElement);
    // intercept the collection
    SortedSet<E> headSet(E toElement);
    // intercept the collection
    SortedSet<E> tailSet(E fromElement);
}

        View the TreeSet class declaration, the source code is as follows:

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
{
}

        View its members, constructors. You can see that it contains a reference to a TreeMap object, and all its constructors are actually encapsulations of different constructors of TreeMap. The source code is as follows:

//NavigableMap<K,V> subclass, refer to a TreeMap
    private transient NavigableMap<E,Object> m;

    //All values ​​in TreeMap use this object
    private static final Object PRESENT = new Object();

    //constructor
    TreeSet(NavigableMap<E,Object> m) {
    	//Specify TreeMap to create TreeSet
        this.m = m;
    }
    public TreeSet() {
    	//Create TreeMap in natural order, then create TreeSet
        this(new TreeMap<E,Object>());
    }
    public TreeSet(Comparator<? super E> comparator) {
    	/ / Specify the sorting method to create a TreeMap, and then create a TreeSet
        this(new TreeMap<>(comparator));
    }
    public TreeSet(Collection<? extends E> c) {
    	//Create default TreeMap and TreeSet, and then add all set elements to TreeMap
        this();
        addAll(c);
    }
    public TreeSet(SortedSet<E> s) {
    	//Create TreeMap and TreeSet through the sorting method specified by the ordered Set and all its elements
        this(s.comparator());
        addAll(s);
    }

        Other methods are relatively simple. Basically, the bottom layer of all interface methods is implemented by the related methods of the called TreeMap object. The source code is as follows:

// batch add
    public  boolean addAll(Collection<? extends E> c) {
        //limit c to one of three types
        if (m.size()==0 && c.size() > 0 && c instanceof SortedSet && m instanceof TreeMap) {
            SortedSet<? extends E> set = (SortedSet<? extends E>) c;
            TreeMap<E,Object> map = (TreeMap<E, Object>) m;
            Comparator<?> cc = set.comparator();
            Comparator<? super E> mc = map.comparator();
            if (cc==mc || (cc != null && cc.equals(mc))) {
                map.addAllForTreeSet(set, PRESENT);
                return true;
            }
        }
        return super.addAll(c);
    }
    
    // iterator
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }
    // descending iterator
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }
    // parallel iterator
    public Spliterator<E> spliterator() {
        return TreeMap.keySpliteratorFor(m);
    }
    

    //Return ordered (descending) Set
    public NavigableSet<E> descendingSet() {
        return new TreeSet<>(m.descendingMap());
    }

    public int size() {
        return m.size();
    }

    public boolean isEmpty() {
        return m.isEmpty();
    }
  
    public boolean contains(Object o) {
        return m.containsKey(o);
    }
   
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

    public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }

    public void clear() {
        m.clear();
    }

    public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                                  E toElement,   boolean toInclusive) {
        return new TreeSet<>(m.subMap(fromElement, fromInclusive,
                                       toElement,   toInclusive));
    }

    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
        return new TreeSet<>(m.headMap(toElement, inclusive));
    }

    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
        return new TreeSet<>(m.tailMap(fromElement, inclusive));
    }

    public SortedSet<E> subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }

    public SortedSet<E> headSet(E toElement) {
        return headSet(toElement, false);
    }

    public SortedSet<E> tailSet(E fromElement) {
        return tailSet(fromElement, true);
    }

    public Comparator<? super E> comparator() {
        return m.comparator();
    }

    public E first() {
        return m.firstKey();
    }

    public E last() {
        return m.lastKey();
    }

    public E lower (E e) {
        return m.lowerKey(e);
    }

    public E floor (E e) {
        return m.floorKey(e);
    }

    public E ceiling (E e) {
        return m.ceilingKey(e);
    }

    public E higher (E e) {
        return m.higherKey(e);
    }

    public E pollFirst() {
        Map.Entry<E,?> e = m.pollFirstEntry();
        return (e == null) ? null : e.getKey();
    }

    public E pollLast() {
        Map.Entry<E,?> e = m.pollLastEntry();
        return (e == null) ? null : e.getKey();
    }

    @SuppressWarnings("unchecked")
    public Object clone() {
        TreeSet<E> clone;
        try {
            clone = (TreeSet<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }

        clone.m = new TreeMap<>(m);
        return clone;
    }

   
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden stuff
        s.defaultWriteObject();

        // Write out Comparator
        s.writeObject(m.comparator());

        // Write out size
        s.writeInt(m.size());

        // Write out all elements in the proper order.
        for (E e : m.keySet())
            s.writeObject(e);
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden stuff
        s.defaultReadObject();

        // Read in Comparator
        @SuppressWarnings("unchecked")
            Comparator<? super E> c = (Comparator<? super E>) s.readObject();

        // Create backing TreeMap
        TreeMap<E,Object> tm = new TreeMap<>(c);
        m = tm;

        // Read in size
        int size = s.readInt();

        tm.readTreeSet(size, s, PRESENT);
    }










Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325448591&siteId=291194637