HashSet

发布时间 2023-11-09 09:43:42作者: anpeiyong

概述

This class implements the <tt>Set</tt> interface, backed by a hash table (actually a <tt>HashMap</tt> instance).
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
This class permits the <tt>null</tt> element.

hashSet基于hashmap实现;

不保证iterator顺序;

hashSet允许null

 

This class offers constant time performance for the basic operations (<tt>add</tt>, <tt>remove</tt>, <tt>contains</tt> and <tt>size</tt>), assuming the hash function disperses the elements properly among the buckets.
Iterating over this set requires time proportional to the sum of the <tt>HashSet</tt> instance's size (the number of elements) plus the "capacity" of the backing <tt>HashMap</tt> instance (the number of buckets).
Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.

hashSet为add、remove、contains、size操作提供O(1)时间复杂度,因为hash将元素分散到buckets;

iterator需要的时间与size成正比;

 

Note that this implementation is not synchronized.
If multiple threads access a hash set concurrently, and at least one of the threads modifies the set, it <i>must</i> be synchronized externally.
This is typically accomplished by synchronizing on some object that naturally encapsulates the set.

If no such object exists, the set should be "wrapped" using the {@link Collections#synchronizedSet Collections.synchronizedSet} method.  

线程非同步的;

如果多个线程并发访问hashset,需要在外部进行同步;

可以使用Collections.synchronizedSet;

 

The iterators returned by this class's <tt>iterator</tt> method are <i>fail-fast</i>:

if the set is modified at any time after the iterator is created, in any way except through the iterator's own <tt>remove</tt> method, the Iterator throws a {@link ConcurrentModificationException}.
Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

hashset的iterator方法是fail-fast;

如果在iterator时,hashSet被修改(除了iterator的remove),将会抛出ConcurrentModificationException;

 

链路

add(E e)

// java.util.HashSet.add
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;                   // hashSet的元素是在hashmap的key,v永远是一个Object对象
    }
    
    // java.util.HashMap.put
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

  

iterator

Set<String> set = new HashSet<>(10);

        set.add("a");set.add("b");set.add("c");set.add("d");
        

        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()){
            String next = iterator.next();
            if (next.equals("a")){
                set.remove(next);
            }
        }

  

iterator.iterator()

  

// set.iterator()                   ->   java.util.HashMap.KeyIterator.iterator()
    // java.util.HashSet.iterator
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    // java.util.HashMap.keySet
    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();
            keySet = ks;
        }
        return ks;
    }

    // java.util.HashMap.KeySet
    final class KeySet extends AbstractSet<K> {
        public final Iterator<K> iterator()     { return new KeyIterator(); }
    }

    // java.util.HashMap.KeyIterator
    final class KeyIterator extends HashIterator implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }

    // java.util.HashMap.HashIterator
    abstract class HashIterator {
        public final boolean hasNext() {
            return next != null;
        }

        final HashMap.Node<K,V> nextNode() {
            HashMap.Node<K,V>[] t;
            HashMap.Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

        public final void remove() {
            HashMap.Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
    }

  

iterator.hasNext()

// iterator.hasNext()       -> java.util.HashMap.HashIterator.hasNext()
    // java.util.HashMap.HashIterator
    abstract class HashIterator {
        public final boolean hasNext() {
            return next != null;
        }
    }

  

iterator.next()

// java.util.HashMap.KeyIterator
    final class KeyIterator extends HashIterator implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }

    // java.util.HashMap.HashIterator
    abstract class HashIterator {
        final HashMap.Node<K,V> nextNode() {
            HashMap.Node<K,V>[] t;
            HashMap.Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }
    }

  

hashSet.remove() 

// java.util.HashSet.remove
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    // java.util.HashMap.remove(java.lang.Object)
    public V remove(Object key) {
        HashMap.Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
    }

    // java.util.HashMap.removeNode
    final HashMap.Node<K,V> removeNode(int hash, Object key, Object value,
                                       boolean matchValue, boolean movable) {
        HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 &&
                (p = tab[index = (n - 1) & hash]) != null) {
            HashMap.Node<K,V> node = null, e; K k; V v;
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                if (p instanceof HashMap.TreeNode)
                    node = ((HashMap.TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key ||
                                        (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            if (node != null && (!matchValue || (v = node.value) == value ||
                    (value != null && value.equals(v)))) {
                if (node instanceof HashMap.TreeNode)
                    ((HashMap.TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;                                                     // 此处remove操作后,++modCount
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

  

set.remove抛ConcurrentModificationException

  当使用iterator迭代时,iterator的next方法有个modCount != expectedModCount,

  而hashset的remove方法执行后有个++modCount,导致二者不一致;