Set interface and the underlying application of its implementation class

Posted by Kenny Pollock on Mon, 22 Nov 2021 13:29:21 +0100

1, Set

The set interface is a sub interface of the Collection. The set interface does not provide additional methods

The Set set cannot contain the same elements. If you try to add two identical elements to the same Set set, the add operation fails.

Set determines whether two objects are the same, not by using the = = operator, but by using the equals() method

Set storage is unordered and cannot store duplicate data.

The Set interface has two implementation classes: HashSet and TreeSet.

2, HashSet

As the main implementation class of Set interface; Thread unsafe; null values can be stored

The bottom layer of HashSet: array + linked list

Construction method
//The parameterless construction method completes the creation of the map
public HashSet() {
    map = new HashMap<>();
}
//The specified set is converted to a HashSet to complete the creation of the map
public HashSet(Collection<? extends E> c) {
   map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
   addAll(c);
}
//Specify initialization size, and load factor
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}
//Specify initialization size
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}
//Specify the initialization size and load factor, dummy has no practical meaning
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

Through the constructor, it can be found that the underlying HashSet is implemented by HashMap.

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

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}

We add element a to the HashSet. First, we call the hashCode() method of the class where element a is located to calculate the hash value of element A. at this time, the hash value then calculates the storage position (i.e. index position) in the underlying array of the HashSet through some algorithm to judge whether there are elements on this position of the array:

If there are no other elements at this location, element a is added successfully

If there are other elements b (or multiple elements in the form of a linked list) at this position, compare the hash values of element a and element b:
If the hash values are different, element a is added successfully
If the hash values are the same, you need to call the equals() method of the class where element a is located:
If equals() returns true, element a addition fails
If equals() returns false, element a is added successfully

static final int hash(Object key) {
        int h;
        // key.hashCode(): returns the hash value, that is, hashcode
        // ^: bitwise XOR
        // >>>: move unsigned right, ignore the sign bit, and fill up the empty bits with 0
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public boolean equals(Object obj) {
        return (this == obj);
}

remove() method
/** 
 * If the specified element exists in this set, it is removed. 
 * More specifically, if this set contains an element e that satisfies (o = = null? e = = null: o.equals (e)), 
 * Remove it. Returns true if this set already contains the element 
 * (Or: if this set is changed due to the call, return true). (once the call returns, the set no longer contains the element). 
 * 
 * The bottom layer actually calls the remove method of HashMap to delete the specified Entry. 
 * @param o Objects that need to be removed if they exist in this set. 
 * @return Returns true if set contains the specified element. 
 */  
public boolean remove(Object o) {  
    return map.remove(o)==PRESENT;  
}  

public boolean remove(Object o) {
	if (o == null) {
		for (int index = 0; index < size; index++)

		if (elementData[index] == null) {
			fastRemove(index);

			return true;
		}
	} else {
		for (int index = 0; index < size; index++)
			if (o.equals(elementData[index])) {
				fastRemove(index);

				return true;
	
		}
	}

	return false;
}

3, LinkedHashSet

LinkedHashSet is a subclass of HashSet. While adding data, each data also maintains two references to record the previous data and the next data of the data

Advantages: LinkedHashSet is more efficient than HashSet for frequent traversal operations

	public void test1(){

        Set set = new HashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(false);

        Iterator iterator = set.iterator();
        while(iterator.hasNext())
        {
            System.out.println(iterator.next());
        }

        System.out.println("******************");
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add("aa");
        linkedHashSet.add("bb");
        linkedHashSet.add("cc");
        linkedHashSet.add("aa");
        linkedHashSet.add("dd");

        Iterator iterator1 = linkedHashSet.iterator();
        while(iterator1.hasNext())
        {
            System.out.println(iterator1.next());
        }
    }
    

Operation results:

4, TreeSet

The elements stored in TreeSet are ordered (not in the order of insertion, but sorted by keyword size), and the elements cannot be repeated.
How to realize ordered storage requires a comparator. In fact, TreeSet pays more attention to non repetition and order. This order requires a compare process. Therefore, parameters are required to implement the Comparable interface.

The sort method depends entirely on the construction method used.
Sort elements using their natural order (natural sort)
Sort according to the Comparator provided when creating the set (Comparator sort)
TreeSet guarantees the ordering and uniqueness of elements
The underlying data structure is red black tree (self balanced binary tree)

    /**
     * Constructs a new, empty tree set, sorted according to the specified
     * comparator.  All elements inserted into the set must be <i>mutually
     * comparable</i> by the specified comparator: {@code comparator.compare(e1,
     * e2)} must not throw a {@code ClassCastException} for any elements
     * {@code e1} and {@code e2} in the set.  If the user attempts to add
     * an element to the set that violates this constraint, the
     * {@code add} call will throw a {@code ClassCastException}.
     *
     * @param comparator the comparator that will be used to order this set.
     *        If {@code null}, the {@linkplain Comparable natural
     *        ordering} of the elements will be used.
     */
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

Topics: Java Back-end