HashMap source code parsing

The previous article introduced the basic data structure of hashMap and put() get() resize() process. Portal

Now let's introduce some of the remaining methods of hashmap
hashMap supports three traversal methods
1 keySet()
The official annotation is @return a set view of the keys contained in this map (key set returned to map)

    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();
            keySet = ks;
        }
        return ks;
    }

This method returns a KeySet class to see what this KetSet is.

   final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

KeySet is an internal class of hashMap, without any logic written. The key to traversing this KeySet is iterator(), which is the core of both iterator and for loop enhancement.
Let's continue to see what KeyIterator.class is.

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

You can see that this iterator inherits from HashIterator, rewriting only the next() method and analyzing it with HashIterator code.

abstract class HashIterator {
       Node<K,V> next;        // next entry to return
       Node<K,V> current;     // current entry
       int expectedModCount;  // for fast-fail
       int index;
       }

First, when the iterator is initialized

       HashIterator() {
            expectedModCount = modCount; //Assign the number of hashMap operations to expectedModCount.
                                        //When other threads operate on the hashMap, the expected and actual values are different and throw exceptions directly, similar to cas.         
            Node<K,V>[] t = table; //Assign the hashMap data set to t
            current = next = null; //Set the current node and the next node of the iterator to be empty
            index = 0;             //hashMap table cursor set to 0
            if (t != null && size > 0) { // advance to first entry to determine whether an empty map is available
                                         //Gets the first non-empty list header node in the table array
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }

ok, now that the initialization of the iterator is complete, the two core methods of the iterator are hasNext() to determine whether there is the next data and next() to get the next data and point next to the next data.
1 hashNext (), it's easy to judge whether next is empty or not.

public final boolean hasNext() {
           return next != null;
       }

2 next(): The abstract class HashIterator does not define this method because next() has a return value and needs a subclass to define the return value itself. HashIterator defines the common operation as the nextNode method.
KeySet's iterator, KeyIterator, defines the following method

public final K next() { return nextNode().key; }

The HashIterator.nextNode () method is as follows. The method is very short, but it operates a lot.

       final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next; //Get next
            if (modCount != expectedModCount) //Determine whether hashMap has been modified by other threads
                throw new ConcurrentModificationException();
            if (e == null)//Determine whether the next node is empty
                throw new NoSuchElementException();
             // The following operations are more complex. Let's split them into multi-step operations.
         /**  current = e;//  Will the current 
              next = current.next; //next The next node assigned to current
              t=table;
              if(next == null&&t != null){//If next is the last node in the list
                 do {} while (index < t.length && (next = t[index++]) == null);
                 // Array cursor++ always finds the head node of the next node with data in the array. If it is the last node, it jumps out of the loop. next =null
              }**/
             if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

2 values()
values().iterator() returns a ValueIterator ()

    final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
    }

The next () method also calls the HashIterator.nextNode() method, but only the value to go.
3 entrySet()
Upper Code

  final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }

Returns the entire node directly
hashMap traversal order
From the nextNode () method, we can see that when hashMap traverses through the three methods mentioned above, it traverses the list from small to large subscripts. If a hash conflict results in a chain table, it traverses the list first. Therefore, the traversal and insertion order of hashMap is different, which is related to hashCode (), traversing has in the insertion order. HMap can use LinkedHashMap, which will continue to be written later.

Keywords: Java

Added by ogge1 on Thu, 25 Jul 2019 11:37:18 +0300