栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

HashMap源码分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

HashMap源码分析

HashMap 底层

在JDK1.8 之前是数组 + 链表,但在JDK1.8 之后变成了数组 + 链表 + 红黑树。当hash表中存储的键值对超过了数组长度乘以负载因子(0.75)时则会进行hash扩容变成原来的两倍,且当链表的长度超过一个阈值(8)的时候则会将链表转为红黑树其时间复杂度时Olog(n)。

HashMap为什么要使用红黑树而不是AVL树

其原因时红黑树和AVL树的查询都是采用二分查找查询效率都比较高,但由于AVL追求绝对的平衡这样在插入和删除的时候会非常耗时,而红黑树其允许局部不平衡,所以在插入和删除的时候其效率高于AVL树

hashMap 扩容会导致死循环?

在Jdk1.8之前,hashMap采用头插法进行扩容,在多线程的情况下如果线程A、B同时对其进行扩容的话因为其采用头差法的方式进行扩容其原因是采用头差法的时会改变原数组entey的指向关系,即a->b->c,采用头差法则会变为c->b->a。
1、 在 A线程进行扩容的时候 获取到 节点a且知道其下一个节点是b了
2、这时被B线程抢去执行权且B线程完成了扩容,这时数据变为 c->b->a
3、此时A线程接着运行则会变为 b -> a,这时由于entey是共享的由于 元数据被改为 c->b->a此时发现b指向的是a了,则A线程执行完之后的数据会变为 a -> b -> a,此时就造成循环了
在Jdk.8之后采用尾插法就避免了这一问题

hash 优化

hash算法优化:对每个hash值的让其高低16位进行异或操作,让其同时保留了高低16为的特性。避免hash冲突。
寻址算法的优化:用与运算替代取模运算。

	static final int hash(Object key) {
        int h;
        // 先获取到hash值,然后异或hash值的右移16为
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

 final Node getNode(int hash, Object key) {
        Node[] tab; Node 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)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;
    }
            
ConcurrentHashMap
  • JDK 1.8之前ConcurrentHashMap采用分段加锁。

[数组1],[数组2],[数组3],[数组4] ----->每个数组的下标都对应一个锁。

  • JDK 1.8之后做了锁粒度优化采用进行CAS+synchronized 进行加锁

[一个大数组],数组里每一个元素进行put操作时都有一个不同的锁,如果两个线程同时操作数组[1]的位置进行put操作,则这时会对数组[1]这个位置进行put的时候会采用CAS的策略。
同一时间只有一个线程能够执行成功这个CAS(Compare And Swage),其它都会执行失败。此时如果put失败则会采用synchorized(数组[1])进行加锁。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/827618.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号