HashMap of put, get the principle of interpretation

HashMap plus an array list used (later after java8 linked list data more than 8, it changed to red-black tree storage) to store the keys, why use an array that HashMap, how the array works, and why add list, and why the list java8 into a red-black tree of it? With such questions take a look at the following analysis:

First talk about the put operation

First on the code, the following code will be detailed analysis

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

      final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

  //代码太多占篇幅,省略掉resize() ;
复制代码

Not looking to source students can HashMap source

Source resolve

  1. From the source code can be seen when you call the put operation is actually putVal called, it will be a key hash calculation, the calculated value of it is this key index in the Node array, so during the operation when it will get to find the corresponding key by the index, the time complexity is O (1), the following operations to look carefully at the putVal.
  2. This means that if the array is empty then put this array to resize () it. Quick overview resize (), if Node array is empty then put it initialized to a load factor of 0.75 (default), length of 16 (the default) array, otherwise it will increase the current array Array twice as before. Note that, with the length of the array 16 while its threshold, but only 16 * 12 = 0.75 (= capacity factor * load threshold value), as long as the elements in the array will exceed the threshold for a resize (), twice expansion .
if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
复制代码
  1. And the length of the hash value by -1 as an index to locate with the bit, if the position of no value. Insert a new node is generated.
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
复制代码
  1. Look in detail below else logic, if the previous position has a value
  2. Take a look at the hash value, etc. as well as key value is not equal to the previous value, if all other then that key is the same, will be returned directly before that node => p, to operate the old nodes.
            if (p.hash == hash &&
              ((k = p.key) == key || (key != null && key.equals(k))))
              e = p;
复制代码
  1. If the key value range, it shows there is a collision hash (hash collision is different key, but the situation is the same hash value), then the next I'll look at the current node is not a tree node (red-black tree), if a node on the tree perform a put operation tree node
            else if (p instanceof TreeNode)
              e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
复制代码
  1. If the key value range, and not a tree node, then that is now stored in a linked list, this list will cycle, if the node encountered is empty, it will be inserted into the node, the number of nodes after you insert more than 8, then this list will be tree. If you have encountered the same key value in the cycle chain process, and directly back to the old node.
          else {
              for (int binCount = 0; ; ++binCount) {
                  if ((e = p.next) == null) {
                      p.next = newNode(hash, key, value, null);
                      if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                          treeifyBin(tab, hash);
                      break;
                  }
                  if (e.hash == hash &&
                      ((k = e.key) == key || (key != null && key.equals(k))))
                      break;
                  p = e;
              }
          }
复制代码
  1. After a big wave and turns, finally got the required insertion position of the node in the array, but this time also need to look at, this location is not the data already exists, and if there is put before that data back to you, if empty it means you can insert the data you want to insert a. We have gained the position of the insertion of data, this time the position may be empty, it may not be empty.
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
复制代码
  1. put the last operation, there is a modCount, it is the number of times the recorded data is modified, why the existence of this number, brief mention here, since HashMap is not thread-safe, if the HashMap using an iterator but other threads modify this Hashmap , there will be ConcurrentModificationException, this exception is thrown because this will modify the number assigned to the iterator, when the other thread went modify HashMap will result in inconsistent data, so use this to modify the number of times when generating HashMap iterator It is a different type of security thread that fail-fast strategy.
  2. Next, look size (refer to the current HashMap of size), because you want to insert the node into an array so the first increment, if the super-threshold array to be doubled. afterNodeInsertion (evict), the Internet search a bit, in order to inherit the HashMap LinkedHashMap class services, afterNodeInsertion method LinkedHashMap are covered for the first callback object is removed into the Map. Put end to this operation.
  ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
复制代码

Call, said so much to finally get the

I would first explain the code

    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
      final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
复制代码
  1. Also get the operation is done by calling getNode method, or the old rules prior to key the hash calculation, passed into the function, the return value getNode function returns.
  2. If the first determination, if the table is empty returns a null value
    if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
复制代码
  1. hash value and the key value is not null first went to look at the first position of the nodes is the same (why go to look at first, because the probability of the existence hash collision is small, usually the number of nodes in a linked list, no need to loop through the entire list, the first direct look at the first node it wants, here is the interest of efficiency)
             if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
复制代码
  1. Of course, if more than one node in the list you will need to loop through, and if there are multiple hash collision that is captive. If the node is a tree node tree node then use the get method to fetch it wants. To get here is over.
             if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
复制代码

Finally, to answer the very beginning asked why after more than eight java8 linked list data later on changed to red-black tree stored?

This relates to reject service attack, such as by some people to find your hash value collision, to make your HashMap continue to collide, then list the same key positions will continue to grow as you need to in this HashMap when the corresponding location query, it will go super big loop through this list, properties and underground. java8 used instead of the red-black tree over eight nodes linked list, query performance is good upgrade from the original is O (n) to O (logn).

Guess you like

Origin juejin.im/post/5d6a0674f265da03c8153832