The Set interface allows you to conveniently Save the specified type in a variable as a Set type. Set is a Collection that does not contain repeated elements. More specifically, Set does not contain any element pair that meets e1.equals (e2), and contains at most one null element. The underlying storage implementation of the Set interface depends on the implementation of Map. It can be said that the management of elements in the Set interface is the management of keys in the Map. The following describes the implementation classes of various Set interfaces, including HashSet, LinkedHashSet, and TreeSet.
1. HashSet
HashSet is implemented by HashMap at the underlying layer. It stores key-value pairs such as [key-Object constants], and also includes initialCapacity and loadFactor. These two parameters have the same meaning as HashMap, is important. In addition, the elements in the HashSet set are unordered.
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable{ static final long serialVersionUID = -5024744406713321676L; private transient HashMap<E,Object> map; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<E,Object>(); } /** * Constructs a new set containing the elements in the specified * collection. The <tt>HashMap</tt> is created with default load factor * (0.75) and an initial capacity sufficient to contain the elements in * the specified collection. * * @param c the collection whose elements are to be placed into this set * @throws NullPointerException if the specified collection is null */ public HashSet(Collection<? extends E> c) { map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * the specified initial capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hash map * @param loadFactor the load factor of the hash map * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); }
2. LinkedHashSet
LinkedHashSet inherits the HashSet, but its underlying storage uses LinkedHashMap, so the elements in it are inserted in sequence. I can see that the javashashset class only defines four constructor methods and does not see the content related to the linked list. Why does the javashashset internally use the linked list to maintain the element insertion sequence (insertion sequence? Click here to see the construction method HashSet (int initialCapacity, float loadFactor, boolean dummy) of the three parameters. Then we will find that the implementation class used is LinkedHashMap:
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { private static final long serialVersionUID = -2851667679971038690L; /** * Constructs a new, empty linked hash set with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity of the linked hash set * @param loadFactor the load factor of the linked hash set * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } /** * Constructs a new, empty linked hash set with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity of the LinkedHashSet * @throws IllegalArgumentException if the initial capacity is less * than zero */ public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } /** * Constructs a new, empty linked hash set with the default initial * capacity (16) and load factor (0.75). */ public LinkedHashSet() { super(16, .75f, true); }
/** * Constructs a new, empty linked hash set. (This package private * constructor is only used by LinkedHashSet.) The backing * HashMap instance is a LinkedHashMap with the specified initial * capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hash map * @param loadFactor the load factor of the hash map * @param dummy ignored (distinguishes this * constructor from other int, float constructor.) * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); }
3. TreeSet
The underlying storage of TreeSet uses TreeMap, which can extract ordered sequences from the Set. The elements must implement the Comparable interface. Otherwise, they are sorted by default dictionary.
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable{ /** * The backing map. */ private transient NavigableMap<E,Object> m; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a set backed by the specified navigable map. */ TreeSet(NavigableMap<E,Object> m) { this.m = m; } /** * Constructs a new, empty tree set, sorted according to the * natural ordering of its elements. All elements inserted into * the set must implement the {@link Comparable} interface. * Furthermore, all such elements must be <i>mutually * comparable</i>: {@code e1.compareTo(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 (for example, the user * attempts to add a string element to a set whose elements are * integers), the {@code add} call will throw a * {@code ClassCastException}. */ public TreeSet() { this(new TreeMap<E,Object>()); }
Summary:
HashSet is a Set designed for fast search. The elements stored in HashSet must define hashCode (). HashSet has the query speed of HashSet and maintains the element sequence (insertion sequence) using the linked list internally ), when you use the iterator to traverse the Set, the result is displayed in the insert order. The elements must define the hashCode () method. The TreeSet stores the Set in order, and the underlying layer is the tree structure, it can be used to extract ordered sequences from the Set, and the elements must implement the Comparable interface.
In addition, here we will mention the significance of hashcode and equals in Set. When performing a set operation with hash as the underlying layer, the system first searches for the corresponding linked list using hashcode, then traverses the corresponding linked list using equals to compare the key, and finally finds the corresponding value. As mentioned last time, when the hashcode method is poorly designed, the element distribution may be uneven. Then, a large number of equals are called to compare keys, which affects the program execution efficiency.
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
Finally, I would like to raise a frequently asked question during the interview: When the hashcode of the two objects is equal, their equals do not necessarily return true; however, when equals of two objects return true, their hashcode must be the same.