JDK8中HashMap源码分析

JDK7及以前,HashMap底层基于数组(Entry数组)和链表实现,计算hash时发生hash冲突将该节点以链表形式存储,当hash冲突过多会导致单个链表过长,查找节点时将耗费O(n)的查找时间

JDK8:底层基于数组(Node数组)、链表、红黑树实现,当同一hash值的节点数超过阈值(8),链表结构将会被转换为红黑树进行数据存储

底层以Node数组存储,对应TreeNode红黑树的实现

transient Node<K,V>[] table;

当hash冲突节点大于指定阈值8时,将链表结构转换为红黑树进行存储

//由链表转换为红黑树的阈值
static final int TREEIFY_THRESHOLD = 8;

//由红黑树转换为链表的阈值
static final int UNTREEIFY_THRESHOLD = 6;

//哈希表的最小树形化阈值
static final int MIN_TREEIFY_CAPACITY = 64; 

执行put操作源码分析

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;
//当前map无数据,则执行resize()方法,并返回n
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
//当前需要存储键值对的位置没有元素,则将键值对封装为Node存放在该位置即可
    if ((p = tab[i = (n - 1) &amp; hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
    //当前位置已有元素占有
        Node<K,V> e; K k;
    //判断该位置元素的key与要插入元素的key是否一致,一致就直接替换
        if (p.hash == hash &amp;&amp;
            ((k = p.key) == key || (key != null &amp;&amp; key.equals(k))))
            e = p;
    //如果是树形结构,则执行putTreeVal方法
        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);
		   //判断是否大于等于需要转换树形结构的阈值,大于则执行treeifyBin方法将链表转换为红黑树结构
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &amp;&amp;
                    ((k = e.key) == key || (key != null &amp;&amp; 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大于阈值,就执行resize方法扩容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

转换为树形结构的treeifyBin方法分析

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();
//哈希表元素个数超过了树形化的最小阈值 进行树形化 e为该位置链表第一个节点
    else if ((e = tab[index = (n - 1) &amp; 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);
    }
}

将二叉树转换为红黑树treeify方法分析

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;
            //遍历所有节点与当前节点x进行比较,调整位置
            for (TreeNode<K,V> p = root;;) {
                int dir, ph;
                K pk = p.key;
                //当前比较节点哈希值比x大 dir = -1
                if ((ph = p.hash) > h)
                    dir = -1;
                //当前比较节点哈希值比x小 dir = 1
                else if (ph < h)
                    dir = 1;
                else if ((kc == null &amp;&amp;
                          (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的父节点
                    x.parent = xp;
                    //将当前比较节点的哈希值与x比较 比x大则x为左节点,否则x为右节点
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    root = balanceInsertion(root, x);
                    break;
                }
            }
        }
    }
    moveRootToFront(tab, root);
}

&nbsp;

文章来源:

Author:LaravelShao
link:https://my.oschina.net/LaravelShao/blog/1799259