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

HashMap底层原理

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

HashMap底层原理

首先来说一说HashMap的结构,在jdk1.8之前是数组加链表,为什么是数组加链表,因为这样可以有效解决hash碰撞问题,java中就使用“拉链法”来解决hash碰撞问题。

一.hash碰撞:简单来说就是

1.HashMap中的Key和对key做一个hashcode()的计算后得到的它在bucket数组中的位置相同时,注意,准确的说是hash计算后得到数组中的位置,(当然,表达成hashcode值相同时也对,但是根本是得到在数组中的位置)也就是数组下标相同时,产生hash碰撞,这个时候就会替换原来的值。(这也就是为什么HashMap的Key是唯一的)

2.那如果只是hash后的值相同,而key不同的话,就会用"拉链法"将要存的放在后续链表中。

好,那么如果只是使用数组加链表的形式,如果产生hash碰撞将元素链到链表中时,也不能保证完全分布均匀,如果一个链表长度过长,那对应的查找元素的时间就会增加,所以jdk1.8之后就加入了红黑树,当链表长度达到阈值8的时候就会将链表转换成红黑树,查找的时间复杂度就会降低。如果说链表中有n个元素,链表的遍历时间复杂度就是O(n),红黑树查找的时间复杂度就是O(logn)。那么查找的时间复杂度就会降低。

二.jdk1.8中对hash算法和寻址算法的优化

之前在对key进行hash(key)计算时,采用的是取模运算,而优化后采用的是寻址算法,即:(n-1) & hash 『n为数组长度』,为什么要使用寻址算法呢?首先 「hash & (n-1)」 效果跟 hash 对 n 取模的效果是一样的, 但是『&』与运算的性能要优于 hash 对 n 取模。

三.那么HashMap中是怎么进行put操作的呢?

首先hash(key),得到hash值在通过『&』与运算((与Key.hashCode的高16位做异或运算)),得到了数组存储位置bucket。

散列表为空,就初始化散列表。

没有发生碰撞就直接添加到相应位置。

发生碰撞:

        1:若key地址相同或者equals后内容相同,则替换旧值

        2:如果是红黑树结构,就调用树的插入方法

        3:链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之后判断链表个数是否到达变成红黑树的阙值8;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。

如果桶满了大于阀值,则resize进行扩容

四.hashmap扩容

当数组满之后会进行扩容,扩容为原来的两倍。

扩容后还要重新对每个hash值进行寻址,也就是用每个hash值跟新的数组长度 n-1 进行『&』与运算操作。

补充:扩容之后的与运算可能会导致之前的发生hash冲突的元素不再发生冲突。

HashMap 在多线程是不安全的,为啥呢?或为什么说HashMap可能会导致死循环?

这个过程就是发生在扩容阶段,在jdk1.7之前,hashmap在扩容2倍,得到新的数组,会将原来的元素重新hash放入到新的扩容后的数组里面去,因为采用的是头插法,头插法就是总是把新增结点插在头部,会造成链表翻转形成闭环,也就是形成死循环。所以会导致不安全

jdk1.8之后虽然是尾插法,但是两个线程或多个线程同时插入元素,如果元素插入同一个位置,有可能导致覆盖。

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

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

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