JDK1.8HashMap源码阅读(持续更新)

 

HashMap在JDK1.8中的存放数据结构的示意图,就是HashMap本质上是一个Node<K, V>数组table,table中每个index对应一个bucket,这个bucket可能是链表,可能是红黑树。

https://i-blog.csdnimg.cn/blog_migrate/ca414ff50efe1e55ac273d780f7d327e.png

  1. 类定义:

类HashMap<K,V>继承了AbstractMap,实现了Map<K,V>、Cloneable和Serializable;

  1. 类中静态常量:

DEFAULT_INITIAL_CAPACITY:默认的初始化容积为16,如果在使用之前就知道具体会多大,建议直接在创建的时候就定义到那个大小,可以避免多次膨胀;

MAXIMUM_CAPACITY:HashMap最大的容积,大小为1 << 30,即为2^31;

DEFAULT_LOAD_FACTOR:客座率,也就是超过现有的容积的多少比例后直接膨胀;

TREEIFY_THRESHOLD:bucket中的节点数到了多少之后变成红黑树,一般建议是2~8之间的数值,越小,则链表变成红黑树越频繁,越大,则查找速度越慢,需要均衡。

UNTREEIFY_THRESHOLD:一个树的链表还原阈值,当扩容的时候,如果bucket中的元素的个数小于这个值的时候,就还原成链表

MIN_TREEIFY_CAPACITY:哈希表的最小树形化容量,当table数组的长度大于这个值的时候,表中的桶才能树形化,否则桶内元素太多时会扩容,而不是树形化,为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD

  1. HashMap中实现添加键值对的方法java.util.HashMap#put调用的是java.util.HashMap#putVal,详细查看其中的代码:

详情参考的URL为:

http://www.cnblogs.com/huaizuo/p/5371099.html

 

  1. HashMap中的java.util.HashMap#get,其实现为先计算key的hash,再从table中获取对应的bucket,再从bucket中的链表或者红黑树中遍历获取对应的value,因此,耗时和链表长度有关,也和红黑树的大小有关。调用的函数是java.util.HashMap#getNode;

  2. HashMap中的java.util.HashMap#containsKey函数,其实现原理和上get一样的,一样的调用的java.util.HashMap#getNode,根据返回结果是否为空来进行判断的。

详细源码和注释如下:

先查看HashMap的put函数,发现他调用的是另一个函数java.util.HashMap#putVal,该函数如下,详情请看注释

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为空,则直接调用resize()函数,初始化存储空间
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //计算插入位置,计算方式为下标为(n - 1) & hash
        if ((p = tab[i = (n - 1) & hash]) == null)//如果插入位置没有其他元素,直接插入,p为插入位置的Node
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //如果插入位置的Node的key和要插入的key一致,则两者相等
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)//如果插入处的Node即p为TreeNode,也就是说是红黑树,则调用putTreeVal
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {//当前插入处的Node仍然为链表,则进行以下操作
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {//判断是否到了bucket的最后一个Node
                        p.next = newNode(hash, key, value, null);
                        //超过了链表设置的长度,就开始扩容,调用treeifyBin函数
                        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;
                }
            }
            //e如果不为空,即为HashMap中存在要插入的键值对的key,更新值
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //如果再插入一个对象后,size超过容积*容积率则扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 从上面代码可以发现,HashMap中的table是一个Node<K, V>数组,而且,table[index]可能是一个链表,也可能是一个树(TreeNode),这个树其实是一颗红黑树。也从上述代码可以知道,一开始存放的是链表,那么什么时候变成的红黑树的呢?我们注意到了treeifyBin()函数,这个函数如下:

final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        //如果table为空,或者table的长度小于MIN_TREEIFY_CAPACITY,则进行resize()
        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);
        }
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值