JDK parsing of Java source code package util: map collections of source Analysis

       map provides a set of mappings between us, which is in the form of a storage element (entry <k, v> object), a value corresponding to each key. In practice, in order to allow us to process this data and even more good form, java jdk offers many map collections correspond to different scenarios. The major ones are hashmap, treemap, linkhashmap, concurrenthashmap is a collection of four main categories of our analysis today.

Class map is defined as follows:

  • Interpretation of the existence of each element of key-value pairs, to access a set of key values.
  • Each key corresponds to only one value, but can each value corresponding to a plurality of keys
  • Each key can not be stored in the same, the same will be the value corresponding coverage

Before we start to see that one of the classes of their inheritance

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

  Map <K, V>, AbstractMap as the interface and inherits its abstract method, a series of specifications to map collections.

1.AbstractMap

       As a map of an abstract class, to map to achieve most of the way, most of the methods are invoked through a method called abstract entrySet to get a Set <Entry <K, V >> collection operation, so we just need to entrySet inherit it and implement methods, you can create a map class.

2.Entry<k,v>

     Map.Entry <k, v> of the embedded interface class, which is used to achieve the saving of keys and values. You can return the object by reading the entry key to the disposable

2.HashMap

    hashmap for routine deletion insertion, but the output sequence is disordered.  

    hashMap collection object instance is a node to the memory cell, node class is the Entry <k, v> class is a static class interior. Source node as follows:

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;  //哈希值
        final K key;   //键
        V value;   //值
        Node<K,V> next;  //指向下一个元素

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

//....

     public final boolean equals(Object o) {
            if (o == this)  //地址是否相等
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))  //判断键和值是否相等
                    return true;
            }
            return false;
        }
}

    In the hashmap when we want to insert a key-value pair, the first hash value is calculated in the key, to calculate the hash value of the array index by targeting the corresponding object and, if the current object does not exist, a new node objects, whereas the presence of an update operation will be determined. It put the source code and comments as follows:

/**
     * Implements Map.put and related methods
     *
     * @param hash key的哈希值
     * @param key the key
     * @param value  
     * @param onlyIfAbsent 如果是true 不能替换存在值
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */ 
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)  //如果为空,则调用resize创建集合对象,初始化阀值
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)  //根据hash值算出数组下标并返回table中对应的对象,为空则创建新的节点
            tab[i] = newNode(hash, key, value, null);
        else {   //如果不为空
            Node<K,V> e; K k;   //e定义为需要更新的节点
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))) //判断当前节点是否是需要更新的节点,如果是则使e指向当前节点
                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) // 当链表内的元素大于等于TREEIFY_THRESHOLD(默认值为8) 会将其转换成树结构
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))  //如果当前节点的hash和key相等则停止循环
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key  e指向节点不为空
                V oldValue = e.value;    
                if (!onlyIfAbsent || oldValue == null) //如果可以覆盖旧节点值则更新
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)  //threshold为需要扩容的阀值
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 

 

 

       The above is in jdk8 hashmap source, and the main difference is jdk7 chain portion, when an element exceeds TREEIFY_THRESHOLD list will change when a tree structure (treeifyBin method) is optimized version jdk7

       In addition there is a hashmap HashTable collection class, his usage is very similar and is based on the hash value and key position elements storage, the difference is not HashTable list structure, only the linear structure, and he is thread-safe. This class is for compatibility with older versions of the function will be preserved, so the official notes with the following passage:

* Java Collections Framework</a>.  Unlike the new collection
* implementations, {@code Hashtable} is synchronized.  If a
* thread-safe implementation is not needed, it is recommended to use
* {@link HashMap} in place of {@code Hashtable}.  If a thread-safe
* highly-concurrent implementation is desired, then it is recommended
* to use {@link java.util.concurrent.ConcurrentHashMap} in place of
* {@code Hashtable}.

     Roughly meaning HashTable is synchronized, if not thread-safe, then recommend the use of HashMap, if it is recommended for use in highly concurrent environments with hope instead of HashTable ConcurrentHashMap

 

3.LinkHashMap

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>{

    /**
     * 记录链表头部
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * 链表尾部
     */
    transient LinkedHashMap.Entry<K,V> tail;

...
}

      HashMap inherited achieve a double linked list function, he adds an element node class before and after the two variables used to record the last and next element.

 static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

       He rewrote the HashMap newCode method, insertion of an additional list of completion of the recording operation

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);  //记录链表操作
        return p;
    }

      linkNodeLast follows

  private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;  
        tail = p;   //设置链表尾部位置
        if (last == null)  //如果是第一个则记录头部
            head = p;
        else {  //记录节点的父节点和子节点
            p.before = last;  
            last.after = p;
        }
    }

    Complete list operation.

 4.TreeMap

     TreeMap classes are ordered, sorted according to the internal elements of natural order or the comparator. Red-black binary tree data structure. Red-black binary tree main features are:

1. The root node (root) black

2. leaf node is always black (null default is black)

3. a node is red then his parent and child nodes can not be red

4. The path a node to any child nodes of the leaves contain the same number of black nodes

    TreeMap class put the main idea is: first red-black binary tree traversal; query whether there is a node containing Key, if there is a node value is updated, if there is no leaf node is inserted, and calls fixAfterInsertion (Entry <k, v> e) The method of adjustment of the tree structure; PUT code is mainly as follows:

public V put(K key, V value) {
        Entry<K,V> t = root;  
        if (t == null) {  //如果是第一个
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) { //如果设置了比较器遍历树 查找等于key的节点
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)  //如果没有比较器则key不能为null
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;  //获取key的Comparable对象
            do {  //查找对于key的节点
                parent = t;
                cmp = k.compareTo(t.key);  
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        } 
        //如果没有找到key的节点则创建一个节点
        Entry<K,V> e = new Entry<>(key, value, parent);  
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);  //平衡红黑二叉树
        size++;
        modCount++;  //操作次数
        return null;
    }
fixAfterInsertion 方法
  每当插入一个节点时会有以下情况发生
情况一:父节点为黑色,则直接插入无需操作
情况二:当父节点为红色,其兄弟节点为红色时,此时插入点为红色违反特征3,则将父节点及其兄弟节点设置成黑色,爷爷节点设置成红色,
       保证子树规则成立
情况三:当父节点为红色,其兄弟节点为黑色时,设置父节点为黑色,爷爷节点为红色,则将爷爷节点向兄弟节点的方向旋转(如果兄弟节点
       是右节点,则需要插入节点是左节点才能对爷爷节点做旋转,如果节点是右节点则需要父节点进行一次旋转),此时会得到一个黑色
       节点下有两个红色节点的结果,保证子树规则成立。

   /** From CLR */
    private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;  //第一次插入的是红色

        while (x != null && x != root && x.parent.color == RED) {  //向下遍历不符合特征3的节点
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //如果父节点是左边节点
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));  
                if (colorOf(y) == RED) {  //父节点的兄弟节点是红色时无需旋转操作
                    setColor(parentOf(x), BLACK); //将父节点和父节点的兄弟节点设置成黑色,爷爷节点设置成红色,保证符合特征3
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {  //如果是黑色证明为null节点,因为如果不是null就违反特征4
                    if (x == rightOf(parentOf(x))) { //当新节点是右叶子节点插入时旋转父节点
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x))); //旋转爷爷节点,结果变成了一个黑节点下存在两个红节点的子树
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;  //设置根节点为黑色,有可能会应为多次变换后根节点变成红色
    }
 

  The above is my analysis of several commonly used map for map insertion method, inadequacies also please gets advice, thank you

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/mxy88888/article/details/90761607