JDK1.8HashMap源码分析

 

基本实现原理

首先有一个每个元素都是链表的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。当链表数组的容量超过初始容量的0.75时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中。

通俗易懂,数组+链表+红黑树实现(jdk1.8)。

核心参数

定义数组表

//存放K值的位置,初始化数组  
transient Node<K,V>[] table;    
transient Set<Map.Entry<K,V>> entrySet;  
//map的大小,存放数据的容量大小  
transient int size;  
//修改时使用,保证线程安全(主要作用),被修改的次数fast-fail机制  
transient int modCount;  
// 阈值 ,容量*负载系数  
int threshold;  
//hash table(哈希表) 的负载因子  
final float loadFactor;  

 

负载因子

//默认负载因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

初始容量

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4// aka 16  

最大容量

static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量

红黑树转化参数

  //当add一个元素到某个元素的时候,其链表长度达到8时并且数组容量大于等于64的时候将链表转换为红黑树    
static final int TREEIFY_THRESHOLD = 8;    
static final int UNTREEIFY_THRESHOLD = 6;    
static final int MIN_TREEIFY_CAPACITY = 64; 

 

核心数据结构

Node数据结构

定义一个Node数据类型,Node是单向链表,它实现了Map.Entry接口。基本图形如下图所示:

static class Node<K,V> implements Map.Entry<K,V> {    
        final int hash; //hash值(保存hash值,在扩容的时候用)    
        final K key; //key值    
        V value; //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 K getKey()        { return key; }    
        public final V getValue()      { return value; }    
        public final String toString() { return key + "=" + value; }    
        
        public final int hashCode() {    
            return Objects.hashCode(key) ^ Objects.hashCode(value);    
        }    
        
        public final V setValue(V newValue) {    
            V oldValue = value;    
            value = newValue;    
            return oldValue;    
        }    
      //判断两个node是否相等,若key和value都相等,返回true。可以与自身比较为true  
        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;    
        }    
    }  

 

TreeNode红黑树链表

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {  
    TreeNode<K,V> parent;  // 父节点  
    TreeNode<K,V> left;    //左子树  
    TreeNode<K,V> right;   //右子树  
    TreeNode<K,V> prev;    // needed to unlink next upon deletion  
    boolean red;           //颜色属性  
    TreeNode(int hash, K key, V val, Node<K,V> next) {  
        super(hash, key, val, next);  
    }  
  
    /** 
     * 返回当前节点的根节点
     */  
    final TreeNode<K,V> root() {  
        for (TreeNode<K,V> r = this, p;;) {  
            if ((p = r.parent) == null)  
                return r;  
            r = p;  
        }  
    }  
  
    /** 
     * Ensures that the given root is the first node of its bin. 
     */  
    static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {  
        int n;  
        if (root != null && tab != null && (n = tab.length) > 0) {  
            int index = (n - 1) & root.hash;  
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index];  
            if (root != first) {  
                Node<K,V> rn;  
                tab[index] = root;  
                TreeNode<K,V> rp = root.prev;  
                if ((rn = root.next) != null)  
                    ((TreeNode<K,V>)rn).prev = rp;  
                if (rp != null)  
                    rp.next = rn;  
                if (first != null)  
                    first.prev = root;  
                root.next = first;  
                root.prev = null;  
            }  
            assert checkInvariants(root);  
        }  
    }  
  
    /** 
     * Finds the node starting at root p with the given hash and key. 
     * The kc argument caches comparableClassFor(key) upon first use 
     * comparing keys. 
     */  
    final TreeNode<K,V> find(int h, Object k, Class<?> kc) {  
        TreeNode<K,V> p = this;  
        do {  
            int ph, dir; K pk;  
            TreeNode<K,V> pl = p.left, pr = p.right, q;  
            if ((ph = p.hash) > h)  
                p = pl;  
            else if (ph < h)  
                p = pr;  
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))  
                return p;  
            else if (pl == null)  
                p = pr;  
            else if (pr == null)  
                p = pl;  
            else if ((kc != null ||  
                      (kc = comparableClassFor(k)) != null) &&  
                     (dir = compareComparables(kc, k, pk)) != 0)  
                p = (dir < 0) ? pl : pr;  
            else if ((q = pr.find(h, k, kc)) != null)  
                return q;  
            else  
                p = pl;  
        } while (p != null);  
        return null;  
    }  
  
    /** 
     * Calls find for root node. 
     */  
    final TreeNode<K,V> getTreeNode(int h, Object k) {  
        return ((parent != null) ? root() : this).find(h, k, null);  
    }  
  
    /** 
     * Tie-breaking utility for ordering insertions when equal 
     * hashCodes and non-comparable. We don't require a total 
     * order, just a consistent insertion rule to maintain 
     * equivalence across rebalancings. Tie-breaking further than 
     * necessary simplifies testing a bit. 
     */  
    static int tieBreakOrder(Object a, Object b) {  
        int d;  
        if (a == null || b == null ||  
            (d = a.getClass().getName().  
             compareTo(b.getClass().getName())) == 0)  
            d = (System.identityHashCode(a) <= System.identityHashCode(b) ?  
                 -1 : 1);  
        return d;  
    }  
  
    /** 
     * Forms tree of the nodes linked from this node. 
     * @return root of tree 
     */  
    final void treeify(Node<K,V>[] tab) {  
        TreeNode<K,V> root = null;  
        for (TreeNode<K,V> x = this, next; x != null; x = next) {  
            next = (TreeNode<K,V>)x.next;  
            x.left = x.right = null;  
            if (root == null) {  
                x.parent = null;  
                x.red = false;  
                root = x;  
            }  
            else {  
                K k = x.key;  
                int h = x.hash;  
                Class<?> kc = null;  
                for (TreeNode<K,V> p = root;;) {  
                    int dir, ph;  
                    K pk = p.key;  
                    if ((ph = p.hash) > h)  
                        dir = -1;  
                    else if (ph < h)  
                        dir = 1;  
                    else if ((kc == null &&  
                              (kc = comparableClassFor(k)) == null) ||  
                             (dir = compareComparables(kc, k, pk)) == 0)  
                        dir = tieBreakOrder(k, pk);  
  
                    TreeNode<K,V> xp = p;  
                    if ((p = (dir <= 0) ? p.left : p.right) == null) {  
                        x.parent = xp;  
                        if (dir <= 0)  
                            xp.left = x;  
                        else  
                            xp.right = x;  
                        root = balanceInsertion(root, x);  
                        break;  
                    }  
                }  
            }  
        }  
        moveRootToFront(tab, root);  
    }  
  
    /** 
     * Returns a list of non-TreeNodes replacing those linked from 
     * this node. 
     */  
    final Node<K,V> untreeify(HashMap<K,V> map) {  
        Node<K,V> hd = null, tl = null;  
        for (Node<K,V> q = this; q != null; q = q.next) {  
            Node<K,V> p = map.replacementNode(q, null);  
            if (tl == null)  
                hd = p;  
            else  
                tl.next = p;  
            tl = p;  
        }  
        return hd;  
    }  
  
    /** 
     * Tree version of putVal. 
     */  
    final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,  
                                   int h, K k, V v) {  
        Class<?> kc = null;  
        boolean searched = false;  
        TreeNode<K,V> root = (parent != null) ? root() : this;  
        for (TreeNode<K,V> p = root;;) {  
            int dir, ph; K pk;  
            if ((ph = p.hash) > h)  
                dir = -1;  
            else if (ph < h)  
                dir = 1;  
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))  
                return p;  
            else if ((kc == null &&  
                      (kc = comparableClassFor(k)) == null) ||  
                     (dir = compareComparables(kc, k, pk)) == 0) {  
                if (!searched) {  
                    TreeNode<K,V> q, ch;  
                    searched = true;  
                    if (((ch = p.left) != null &&  
                         (q = ch.find(h, k, kc)) != null) ||  
                        ((ch = p.right) != null &&  
                         (q = ch.find(h, k, kc)) != null))  
                        return q;  
                }  
                dir = tieBreakOrder(k, pk);  
            }  
  
            TreeNode<K,V> xp = p;  
            if ((p = (dir <= 0) ? p.left : p.right) == null) {  
                Node<K,V> xpn = xp.next;  
                TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);  
                if (dir <= 0)  
                    xp.left = x;  
                else  
                    xp.right = x;  
                xp.next = x;  
                x.parent = x.prev = xp;  
                if (xpn != null)  
                    ((TreeNode<K,V>)xpn).prev = x;  
                moveRootToFront(tab, balanceInsertion(root, x));  
                return null;  
            }  
        }  
    }  
  
    /** 
     * Removes the given node, that must be present before this call. 
     * This is messier than typical red-black deletion code because we 
     * cannot swap the contents of an interior node with a leaf 
     * successor that is pinned by "next" pointers that are accessible 
     * independently during traversal. So instead we swap the tree 
     * linkages. If the current tree appears to have too few nodes, 
     * the bin is converted back to a plain bin. (The test triggers 
     * somewhere between 2 and 6 nodes, depending on tree structure). 
     */  
    final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,  
                              boolean movable) {  
        int n;  
        if (tab == null || (n = tab.length) == 0)  
            return;  
        int index = (n - 1) & hash;  
        TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;  
        TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;  
        if (pred == null)  
            tab[index] = first = succ;  
        else  
            pred.next = succ;  
        if (succ != null)  
            succ.prev = pred;  
        if (first == null)  
            return;  
        if (root.parent != null)  
            root = root.root();  
        if (root == null || root.right == null ||  
            (rl = root.left) == null || rl.left == null) {  
            tab[index] = first.untreeify(map);  // too small  
            return;  
        }  
        TreeNode<K,V> p = this, pl = left, pr = right, replacement;  
        if (pl != null && pr != null) {  
            TreeNode<K,V> s = pr, sl;  
            while ((sl = s.left) != null) // find successor  
                s = sl;  
            boolean c = s.red; s.red = p.red; p.red = c; // swap colors  
            TreeNode<K,V> sr = s.right;  
            TreeNode<K,V> pp = p.parent;  
            if (s == pr) { // p was s's direct parent  
                p.parent = s;  
                s.right = p;  
            }  
            else {  
                TreeNode<K,V> sp = s.parent;  
                if ((p.parent = sp) != null) {  
                    if (s == sp.left)  
                        sp.left = p;  
                    else  
                        sp.right = p;  
                }  
                if ((s.right = pr) != null)  
                    pr.parent = s;  
            }  
            p.left = null;  
            if ((p.right = sr) != null)  
                sr.parent = p;  
            if ((s.left = pl) != null)  
                pl.parent = s;  
            if ((s.parent = pp) == null)  
                root = s;  
            else if (p == pp.left)  
                pp.left = s;  
            else  
                pp.right = s;  
            if (sr != null)  
                replacement = sr;  
            else  
                replacement = p;  
        }  
        else if (pl != null)  
            replacement = pl;  
        else if (pr != null)  
            replacement = pr;  
        else  
            replacement = p;  
        if (replacement != p) {  
            TreeNode<K,V> pp = replacement.parent = p.parent;  
            if (pp == null)  
                root = replacement;  
            else if (p == pp.left)  
                pp.left = replacement;  
            else  
                pp.right = replacement;  
            p.left = p.right = p.parent = null;  
        }  
  
        TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);  
  
        if (replacement == p) {  // detach  
            TreeNode<K,V> pp = p.parent;  
            p.parent = null;  
            if (pp != null) {  
                if (p == pp.left)  
                    pp.left = null;  
                else if (p == pp.right)  
                    pp.right = null;  
            }  
        }  
        if (movable)  
            moveRootToFront(tab, r);  
    }  
  
    /** 
     * Splits nodes in a tree bin into lower and upper tree bins, 
     * or untreeifies if now too small. Called only from resize; 
     * see above discussion about split bits and indices. 
     * 
     * @param map the map 
     * @param tab the table for recording bin heads 
     * @param index the index of the table being split 
     * @param bit the bit of hash to split on 
     */  
    final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {  
        TreeNode<K,V> b = this;  
        // Relink into lo and hi lists, preserving order  
        TreeNode<K,V> loHead = null, loTail = null;  
        TreeNode<K,V> hiHead = null, hiTail = null;  
        int lc = 0, hc = 0;  
        for (TreeNode<K,V> e = b, next; e != null; e = next) {  
            next = (TreeNode<K,V>)e.next;  
            e.next = null;  
            if ((e.hash & bit) == 0) {  
                if ((e.prev = loTail) == null)  
                    loHead = e;  
                else  
                    loTail.next = e;  
                loTail = e;  
                ++lc;  
            }  
            else {  
                if ((e.prev = hiTail) == null)  
                    hiHead = e;  
                else  
                    hiTail.next = e;  
                hiTail = e;  
                ++hc;  
            }  
        }  
  
        if (loHead != null) {  
            if (lc <= UNTREEIFY_THRESHOLD)  
                tab[index] = loHead.untreeify(map);  
            else {  
                tab[index] = loHead;  
                if (hiHead != null) // (else is already treeified)  
                    loHead.treeify(tab);  
            }  
        }  
        if (hiHead != null) {  
            if (hc <= UNTREEIFY_THRESHOLD)  
                tab[index + bit] = hiHead.untreeify(map);  
            else {  
                tab[index + bit] = hiHead;  
                if (loHead != null)  
                    hiHead.treeify(tab);  
            }  
        }  
    }  
  
    /* ------------------------------------------------------------ */  
    // Red-black tree methods, all adapted from CLR  
  
    static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,  
                                          TreeNode<K,V> p) {  
        TreeNode<K,V> r, pp, rl;  
        if (p != null && (r = p.right) != null) {  
            if ((rl = p.right = r.left) != null)  
                rl.parent = p;  
            if ((pp = r.parent = p.parent) == null)  
                (root = r).red = false;  
            else if (pp.left == p)  
                pp.left = r;  
            else  
                pp.right = r;  
            r.left = p;  
            p.parent = r;  
        }  
        return root;  
    }  
  
    static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,  
                                           TreeNode<K,V> p) {  
        TreeNode<K,V> l, pp, lr;  
        if (p != null && (l = p.left) != null) {  
            if ((lr = p.left = l.right) != null)  
                lr.parent = p;  
            if ((pp = l.parent = p.parent) == null)  
                (root = l).red = false;  
            else if (pp.right == p)  
                pp.right = l;  
            else  
                pp.left = l;  
            l.right = p;  
            p.parent = l;  
        }  
        return root;  
    }  
  
    static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,  
                                                TreeNode<K,V> x) {  
        x.red = true;  
        for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {  
            if ((xp = x.parent) == null) {  
                x.red = false;  
                return x;  
            }  
            else if (!xp.red || (xpp = xp.parent) == null)  
                return root;  
            if (xp == (xppl = xpp.left)) {  
                if ((xppr = xpp.right) != null && xppr.red) {  
                    xppr.red = false;  
                    xp.red = false;  
                    xpp.red = true;  
                    x = xpp;  
                }  
                else {  
                    if (x == xp.right) {  
                        root = rotateLeft(root, x = xp);  
                        xpp = (xp = x.parent) == null ? null : xp.parent;  
                    }  
                    if (xp != null) {  
                        xp.red = false;  
                        if (xpp != null) {  
                            xpp.red = true;  
                            root = rotateRight(root, xpp);  
                        }  
                    }  
                }  
            }  
            else {  
                if (xppl != null && xppl.red) {  
                    xppl.red = false;  
                    xp.red = false;  
                    xpp.red = true;  
                    x = xpp;  
                }  
                else {  
                    if (x == xp.left) {  
                        root = rotateRight(root, x = xp);  
                        xpp = (xp = x.parent) == null ? null : xp.parent;  
                    }  
                    if (xp != null) {  
                        xp.red = false;  
                        if (xpp != null) {  
                            xpp.red = true;  
                            root = rotateLeft(root, xpp);  
                        }  
                    }  
                }  
            }  
        }  
    }  
  
    static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,  
                                               TreeNode<K,V> x) {  
        for (TreeNode<K,V> xp, xpl, xpr;;)  {  
            if (x == null || x == root)  
                return root;  
            else if ((xp = x.parent) == null) {  
                x.red = false;  
                return x;  
            }  
            else if (x.red) {  
                x.red = false;  
                return root;  
            }  
            else if ((xpl = xp.left) == x) {  
                if ((xpr = xp.right) != null && xpr.red) {  
                    xpr.red = false;  
                    xp.red = true;  
                    root = rotateLeft(root, xp);  
                    xpr = (xp = x.parent) == null ? null : xp.right;  
                }  
                if (xpr == null)  
                    x = xp;  
                else {  
                    TreeNode<K,V> sl = xpr.left, sr = xpr.right;  
                    if ((sr == null || !sr.red) &&  
                        (sl == null || !sl.red)) {  
                        xpr.red = true;  
                        x = xp;  
                    }  
                    else {  
                        if (sr == null || !sr.red) {  
                            if (sl != null)  
                                sl.red = false;  
                            xpr.red = true;  
                            root = rotateRight(root, xpr);  
                            xpr = (xp = x.parent) == null ?  
                                null : xp.right;  
                        }  
                        if (xpr != null) {  
                            xpr.red = (xp == null) ? false : xp.red;  
                            if ((sr = xpr.right) != null)  
                                sr.red = false;  
                        }  
                        if (xp != null) {  
                            xp.red = false;  
                            root = rotateLeft(root, xp);  
                        }  
                        x = root;  
                    }  
                }  
            }  
            else { // symmetric  
                if (xpl != null && xpl.red) {  
                    xpl.red = false;  
                    xp.red = true;  
                    root = rotateRight(root, xp);  
                    xpl = (xp = x.parent) == null ? null : xp.left;  
                }  
                if (xpl == null)  
                    x = xp;  
                else {  
                    TreeNode<K,V> sl = xpl.left, sr = xpl.right;  
                    if ((sl == null || !sl.red) &&  
                        (sr == null || !sr.red)) {  
                        xpl.red = true;  
                        x = xp;  
                    }  
                    else {  
                        if (sl == null || !sl.red) {  
                            if (sr != null)  
                                sr.red = false;  
                            xpl.red = true;  
                            root = rotateLeft(root, xpl);  
                            xpl = (xp = x.parent) == null ?  
                                null : xp.left;  
                        }  
                        if (xpl != null) {  
                            xpl.red = (xp == null) ? false : xp.red;  
                            if ((sl = xpl.left) != null)  
                                sl.red = false;  
                        }  
                        if (xp != null) {  
                            xp.red = false;  
                            root = rotateRight(root, xp);  
                        }  
                        x = root;  
                    }  
                }  
            }  
        }  
    }  
  
    /** 
     * Recursive invariant check 
     */  
    static <K,V> boolean checkInvariants(TreeNode<K,V> t) {  
        TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,  
            tb = t.prev, tn = (TreeNode<K,V>)t.next;  
        if (tb != null && tb.next != t)  
            return false;  
        if (tn != null && tn.prev != t)  
            return false;  
        if (tp != null && t != tp.left && t != tp.right)  
            return false;  
        if (tl != null && (tl.parent != t || tl.hash > t.hash))  
            return false;  
        if (tr != null && (tr.parent != t || tr.hash < t.hash))  
            return false;  
        if (t.red && tl != null && tl.red && tr != null && tr.red)  
            return false;  
        if (tl != null && !checkInvariants(tl))  
            return false;  
        if (tr != null && !checkInvariants(tr))  
            return false;  
        return true;  
    }  
}  

 

核心方法

计算hash

static final int hash(Object key) {  
    int h;  
// 如果key为null则放在索引的第一个位置,其他取key.hashCode()的低16位  
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);  
}  

 

根据K获得V值

基本思路:get(key)方法时获取key的hash值,计算hash&(n-1)得到在链表数组中的位置first=tab[hash&(n-1)],先判断first的key是否与参数key相等,不等就遍历后面的链表找到相同的key值返回对应的Value值即可。

1.  //如果存在K则返回对应的V,不存在则返回null    
2.  public V get(Object key) {    
3.      Node<K,V> e;    
4.      return (e = getNode(hash(key), key)) == null ? null : e.value;    
5.  }    
6.  //具体的实现,循环遍历链表,获得K对应的V值    
7.  final Node<K,V> getNode(int hash, Object key) {    
8.      Node<K,V>[] tab; //Entry对象数组  
9.       Node<K,V> first, e; //在tab数组中经过散列的第一个位置  
10.      int n; K k;    
11.     //在一条链上的hash值相同, 找到插入的第一个Node,方法是hash值和n-1相与tab[(n - 1) & hash]  
12.     if ((tab = table) != null && (n = tab.length) > 0 &&    
13.         (first = tab[(n - 1) & hash]) != null) {    
14.      // 检查第一个Node是不是要找的Node  
15.         if (first.hash == hash && // 判断条件是hash值要相同,key值要相同  
16.             ((k = first.key) == key || (key != null && key.equals(k))))    
17.             return first;    
18. //循环遍历下一个节点,检查first后面的node  
19.         if ((e = first.next) != null) {    
20.             if (first instanceof TreeNode)    
21. //红黑树节点    
22.                 return ((TreeNode<K,V>)first).getTreeNode(hash, key);    
23.   /*遍历后面的链表,找到key值和hash值都相同的Node*/  
24.             do {    
25. //非红黑树节点    
26.                 if (e.hash == hash &&    
27.                     ((k = e.key) == key || (key != null && key.equals(k))))    
28.                     return e;    
29.             } while ((e = e.next) != null);    
30.         }    
31.     }    
32.     return null;    
33. }  

 

将K和V放入map中

基本原理:下面简单说下添加键值对put(key,value)的过程:

1.判断键值对数组tab[]是否为空或为null,否则以默认大小resize();

2.根据键值key计算hash值得到插入的数组索引i,如果tab[i]==null,直接新建节点添加,否则转入3

3.判断当前数组中处理hash冲突的方式为链表还是红黑树(check第一个节点类型即可),分别处理

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

 

map中具体的实现put方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,  
               boolean evict) {  
    Node<K,V>[] tab; Node<K,V> p; int n, i;  
//判断初始化table不为null  
    if ((tab = table) == null || (n = tab.length) == 0)  
        n = (tab = resize()).length;  
//如果table的在(n-1)&hash的值是空,就新建一个节点插入在该位置  
    if ((p = tab[i = (n - 1) & hash]) == null)  
        tab[i] = newNode(hash, key, value, null);  
//表示有冲突,开始处理冲突  
    else {  
        Node<K,V> e; K k;  
//检查第一个Node,p是不是要找的值  
        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) {  
//循环遍历链表,如果链表为null情况下,直接存放追加在next后面  
                if ((e = p.next) == null) {  
                    p.next = newNode(hash, key, value, null);  
//如果链表的长度大于8并且数组容量大于等于64的时候,将链表转成红黑树  
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st  
                        treeifyBin(tab, hash);  
                    break;  
                }  
//查找链表中是否存在该key,如果存在key则直接修改vule值  
                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;  
//如果size>12的情况下,提前进行扩容 如果当前大小大于门限,门限原本是初始容量*0.75  
    if (++size > threshold)  
//扩容两倍  
        resize();  
    afterNodeInsertion(evict);  
    return null;  
}  

 

modCount++,HashMap集合线程不安全,

A线程遍历HashMap结果集 B线程遍历HashMap中key值。

modCount在新增的时候增加,Fastclass机制。

扩容之后进行重新赋值

基本原理:构造hash表时,如果不指明初始大小,默认大小为16(即Node数组大小16),如果Node[]数组中的元素达到(填充比*Node.length)重新调整HashMap大小 变为原来2倍大小,扩容很耗时

 //创建新的talbe将原来table中值放入到新的table中。  
final Node<K,V>[] resize() {  
    Node<K,V>[] oldTab = table;  
    int oldCap = (oldTab == null) ? 0 : oldTab.length;  
    int oldThr = threshold;  
    int newCap, newThr = 0;  
    if (oldCap > 0) {  
        if (oldCap >= MAXIMUM_CAPACITY) {  
            threshold = Integer.MAX_VALUE;  
            return oldTab;  
        }  
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&  
                 oldCap >= DEFAULT_INITIAL_CAPACITY)  
//左移1位,2的倍数 把新表的门限设置为旧表门限的两倍newThr=oldThr*2  
            newThr = oldThr << 1; // double threshold  
    }  
    else if (oldThr > 0) // initial capacity was placed in threshold  
        newCap = oldThr;  
    else {               // zero initial threshold signifies using defaults  
        newCap = DEFAULT_INITIAL_CAPACITY;  
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);  
    }  
    if (newThr == 0) {  
        float ft = (float)newCap * loadFactor;  
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?  
                  (int)ft : Integer.MAX_VALUE);  
    }  
    threshold = newThr;  
    @SuppressWarnings({"rawtypes","unchecked"})  
//下面开始构造新表,初始化表中的数据  
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];  
//把新表赋值给table  
    table = newTab;  
//原表不是空要把原表中数据移动到新表中  
    if (oldTab != null) {  
//遍历原来的旧表  
        for (int j = 0; j < oldCap; ++j) {  
            Node<K,V> e;  
            if ((e = oldTab[j]) != null) {  
//解决死循环  
                oldTab[j] = null;  
//判断链表是否到底部, 说明这个node没有链表直接放在新表的e.hash & (newCap - 1)位置  
                if (e.next == null)  
//计算当前key存在新的table中的位置  
                    newTab[e.hash & (newCap - 1)] = e;  
//判断当前node节点是不是红黑树  
                else if (e instanceof TreeNode)  
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);  
  
//如果e后边有链表,到这里表示e后面带着个单链表,需要遍历单链表,将每个结点重新放  
                else { // preserve order  
//如果为链表  
//低位链表  
                    Node<K,V> loHead = null, loTail = null;  
//高位链表  
                    Node<K,V> hiHead = null, hiTail = null;  
                    Node<K,V> next;  
 //新表是旧表的两倍容量,实例上就把单链表拆分为两队,  e.hash&oldCap为低位一队,e.hash&oldCap为高位一对    
                    do {  
                        next = e.next;  
                        if ((e.hash & oldCap) == 0) {  
//0放低位  
                            if (loTail == null)  
                                loHead = e;  
                            else  
                                loTail.next = e;  
                            loTail = e;  
                        }  
//16放高位  
                        else {  
                            if (hiTail == null)  
                                hiHead = e;  
                            else  
                                hiTail.next = e;  
                            hiTail = e;  
                        }  
                    } while ((e = next) != null);  
//将2个链表放到新的位置  
//低位链表不为null,放在新表原位置  
                    if (loTail != null) {  
                        loTail.next = null;  
//原来的位置  
                        newTab[j] = loHead;  
                    }  
//高位链表不为null,放在新表j+oldCap位置     
                    if (hiTail != null) {  
                        hiTail.next = null;  
//16+j的位置  
                        newTab[j + oldCap] = hiHead;  
                    }  
                }  
            }  
        }  
    }  
    return newTab;  
}  

 

链表和红黑树直接进行转换

final void treeifyBin(Node<K,V>[] tab, int hash) {  
    int n, index; Node<K,V> e;  
//一般节点  
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)  
        resize();  
    else if ((e = tab[index = (n - 1) & hash]) != null) {  
//树节点  
        TreeNode<K,V> hd = null, tl = null;  
        do {  
//新建一个树节点  
            TreeNode<K,V> p = replacementTreeNode(e, null);  
            if (tl == null)  
                hd = p;  
            else {  
                p.prev = tl;  
                tl.next = p;  
            }  
            tl = p;  
        } while ((e = e.next) != null);  
        if ((tab[index] = hd) != null)  
            hd.treeify(tab);  
    }  
}  

 

树节点

final void treeify(Node<K,V>[] tab) {  
    TreeNode<K,V> root = null;  
    for (TreeNode<K,V> x = this, next; x != null; x = next) {  
        next = (TreeNode<K,V>)x.next;  
        x.left = x.right = null;  
        if (root == null) {  
            x.parent = null;  
            x.red = false;  
            root = x;  
        }  
        else {  
            K k = x.key;  
            int h = x.hash;  
            Class<?> kc = null;  
            for (TreeNode<K,V> p = root;;) {  
                int dir, ph;  
                K pk = p.key;  
                if ((ph = p.hash) > h)  
                    dir = -1;  
                else if (ph < h)  
                    dir = 1;  
                else if ((kc == null &&  
                          (kc = comparableClassFor(k)) == null) ||  
                         (dir = compareComparables(kc, k, pk)) == 0)  
                    dir = tieBreakOrder(k, pk);  
  
                TreeNode<K,V> xp = p;  
                if ((p = (dir <= 0) ? p.left : p.right) == null) {  
                    x.parent = xp;  
                    if (dir <= 0)  
                        xp.left = x;  
                    else  
                        xp.right = x;  
                    root = balanceInsertion(root, x);  
                    break;  
                }  
            }  
        }  
    }  
    moveRootToFront(tab, root);  
}  

 

扩容

static final int tableSizeFor(int cap) {  
    int n = cap - 1;  
    n |= n >>> 1;  
    n |= n >>> 2;  
    n |= n >>> 4;  
    n |= n >>> 8;  
    n |= n >>> 16;  
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;  
} 

 

HashMap总结

1.1.8用数组+链表+红黑树实现,尾插法,解决1.7put方法多线程死循环。

2.能够降低key对应的index冲突概率,提高查询效率。

3.原来的链表使用与运算的时候,hash&原来table长度,拆分成两个链表放入到新的table中,将链表的长度缩短,能够提高链表的查询效率。

4.当has没有冲突的时候时间复杂度为O(1),当发生hash冲突用链表的时候为O(n),有红黑树的时候为O(InN)。

5.当HashMap中如何1万个key效率最高,计算公式为HashMap初始大小=需要存放元素个数/负载因子+1。

6.当每次扩容都是2的n次方,如果为偶数& hash,index的冲突概率会变大,所以应该变成奇数。i=(n - 1) & hash

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值