1.hashmap的put的原理,hashmap的扩容及计算槽的算法,线程安全的hashtable、ConcurrentHashMap的区别是什么

1.1 hashMap的put原理

什么时候变成红黑树?

当链表的长度为8以及table长度大于64时,变成红黑树(若小于64,则扩容),长度为8也是基于泊松离散分布,一个key中变成链表长度为8的概率很低,另外就是从查询效率方面,红黑树的平均查找长度(为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值)为:log(n) 若长度为8,则log2(8) = 3 ,链表的平均查找长度为8/2 = 4 红黑树效率更高

当红黑树的长度为6时,从红黑树变回链表,不直接用8,是防止数据结构来回变动。

为什么不直接使用红黑树,而是要先使用链表实在不行再转红黑树呢?

因为树节点的大小是链表节点大小的两倍,所以只有在容器中包含足够的节点保证使用才用它

add节点时,jdk7是头插法,而jdk8是尾插法

1.2 hash原理

jdk8:

jdk7:

不管jdk7和8是怎么hash的,但是他们在使用的时候都使这样:

h & (length-1); (这里也可以说明hashMap容量为什么一定是2的n次方,为了方便& 16是经验值

因为同length-1按位与,同 h% length一样,能够计算出来数组的下标,而且按位与效率更高,不需要十进制转换;而且还可以解决hash值为负数的为题,如果是对负数取模,还是比较麻烦(例如:-17 % 10 的计算结果如下:r = (-17) - (-17 / 10) x 10 = (-17) - (-1 x 10) = -7),如果是二进制,length-1肯定是正数,也就是得到的结果一定是正数

在jdk7中把hash值进行了4次右移,是为了对key的hashCode(32位有符号的int值)进行扰动计算,防止不同hashCode的高位不同但低位相同导致的hash冲突。简单点说,就是为了把高位的特征和低位的特征组合起来,降低哈希冲突的概率,也就是说,尽量做到任何一位的变化都能对最终得到的结果产生影响。

Java 8中这一步做了优化,只做一次16位右位移(也就取到了高16位)异或混合,而不是四次,但原理是不变的。

补充hashTable一些内容:

HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。

HashTable默认的初始大小为11,之后每次扩充为原来的2n+1。

当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀,所以单从这一点上看,HashTable的哈希表大小选择,似乎更高明些。因为hash结果越分散效果越好。

在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。所以从hash计算的效率上,又是HashMap更胜一筹。

但是,HashMap为了提高效率使用位运算代替哈希,这又引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改进,进行了扰动计算。

1.3 负载因子为什么是0.75?

负载因子 = 填入表中的元素个数 / 散列表的长度

如果是1时,就说明,这个时候hashmap都满了,然后再扩容,这样肯定会有大量的hash冲突,

如果是0.5时,这个时候还有一半的空间,会造成空间浪费

设置为0.75是“泊松分布(描述单位时间内随机事件发生的次数的概率分布)”,在时间和空间上的折中。

1.4 为什么用红黑树?

红黑树本质上是一棵二叉查找树(左子节点的值小于根节点的值,右子节点的值大于根节点的值),但他在二叉查找树的基础上增加了着色和相关性质,使红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度都是O(logn)

红黑树的5个性质:

根节点是黑色

每个叶子节点是黑色

每个红色节点的所有叶子节点都是黑色

红黑树的左旋:左旋中的“左”,意味着“被旋转的节点将变成一个左节点”

红黑树的右旋:被旋转的节点将变成一个右节点

1.5 hashmap、hashtable、ConcurrentHashmap的区别

  1. hashtable的key不允许有null
  2. concurrenthashmap的key也不能为null
  3. 在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,主要实现原理是实现了锁分离的思路解决了多线程的安全问题。put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Segment里面的Entry,然后在遍历entry链表,Segment实现了ReentrantLock,也就带有锁的功能。当执行put操作时,会进行第一次key的hash来定位Segment的位置,如果该Segment还没有初始化,即通过CAS操作进行赋值,然后进行第二次hash操作,找到相应的HashEntry的位置,这里会利用继承过来的锁的特性,在将数据插入指定的HashEntry位置时(链表的尾端),会通过继承ReentrantLock的tryLock()方法尝试去获取锁,如果获取成功就直接插入相应的位置,如果已经有线程获取该Segment的锁,那当前线程会以自旋的方式去继续的调用tryLock()方法去获取锁,超过指定次数就挂起,等待唤醒.
  4. JDK1.8版本中synchronized+CAS+HashEntry+红黑树,

1.7跟1.8的相比,1.8的数据结构更加简单,使用红黑树优化查询效率。在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,更加的灵活,而在低粒度中,Condition的优势就没有了

1.6 ConcurrentHashmap size的计算方式

jdk7:有两种方案:当第一种方案失败的时候回走第二种方案

第一种方案:使用不加锁的模式去尝试多次计算的segement的modcount(记录数据变化操作的次数),最多三次,比较前后两次计算的结果,结果一致就认为当前没有元素加入,计算的结果是准确的.再返回segement中count值相加的结果。

第二种方案:给每个Segment加上锁,然后计算。

jdk8:

  baseCount + 数组中的每个count

  增加数量的时候,会先cas增加baseCount,如果baseCount增加失败,则会写到数组中,目的是为了减少锁冲突。

HashMap、ConcurrentHashMap对比的更多相关文章

  1. HashTable & HashMap & ConcurrentHashMap 原理与区别

    一.三者的区别     HashTable HashMap ConcurrentHashMap 底层数据结构 数组+链表 数组+链表 数组+链表 key可为空 否 是 否 value可为空 否 是 否 ...

  2. 深入理解HashMap+ConcurrentHashMap的扩容策略

    前言 理解HashMap和ConcurrentHashMap的重点在于: (1)理解HashMap的数据结构的设计和实现思路 (2)在(1)的基础上,理解ConcurrentHashMap的并发安全的 ...

  3. Jdk8 Hashmap ConcurrentHashMap

    JDK1.8 Hashmap JDK1.8 ConcurrentHashMap 不采用segment而采用 synchronized (f)  f = table[i]; 减小锁的力度 设计了MOVE ...

  4. java多线程:并发包中ConcurrentHashMap和jdk的HashMap的对比

    一:HashMap--->底层存储的是Entry<K,V>[]数组--->Entry<K,V>的结构是一个单向的链表static class Entry<K, ...

  5. java多线程之hashmap concurrenthashmap的状态同步

    最近在高并发的系统中发现,concurrenthashmap除了大家熟知的避免循环期间发生ConcurrentModificationException异常外,还有重要的一点是Retrievals r ...

  6. HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  7. Java7/8 HashMap ConcurrentHashMap

    网上关于 HashMap 和 ConcurrentHashMap 的文章确实不少,不过缺斤少两的文章比较多,所以才想自己也写一篇,把细节说清楚说透,尤其像 Java8 中的 ConcurrentHas ...

  8. HashMap? ConcurrentHashMap?

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  9. java面试考点-HashTable/HashMap/ConcurrentHashMap

    HashTable 内部数据结构是数组+链表,键值对不允许为null,线程安全,但是锁是整表锁,性能较差/效率低 HashMap 结构同HashTable,键值对允许为null,线程不安全, 默认初始 ...

随机推荐

  1. Windows应用开发中程序窗口中的各种图标尺寸规划

    为了让你的图标在各个视图模式下都能有合适的尺寸,需要制作4种尺寸16x16.32x32.48x48.256x256 在Windows系统中,几乎所有窗口都是ListView,其中的图标都按照指定的尺寸 ...

  2. Python基础(递归函数)

    def age(n): if n == 1: return 18 else: return age(n - 1) + 2 ret=age(100) print(ret)#216 def test(nu ...

  3. m3u8 ts 视频流爬取思路,合成

    .... 先开调试,输入查找一下有没有 m3u8 文件 然后下下来用Notepad++ 打开一下 (以下的样子) 这里就是整个视频的视频流,  .ts 的都是文件,都下下来, ------------ ...

  4. MyBatis-Plus中如何使用ResultMap

    MyBatis-Plus (简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发.提高效率而生. MyBatis-Plus对MyBatis基本零侵入,完全可以 ...

  5. FastAPI(六十二)实战开发《在线课程学习系统》需求分析

    前言 基础的分享我们已经分享了六十篇,那么我们这次分享开始将用一系列的文章分享实战课程.我们分享的系统是在线学习系统.我们会分成不同的模块进行分享.我们的目的是带着大家去用fastapi去实战一次,开 ...

  6. C# Pechkin初始化一次后被锁住的问题

    Pechkin.dll可用于pdf的生成,常规用法网上都有介绍:https://www.cnblogs.com/felixnet/p/5143934.html 但是当在一个页面上执行过一次之后,再次就 ...

  7. IPv6 寻址方式简介

     在计算机网络中,寻址模式是指在网络上托管地址的机制.IPv6 提供了多种类型的模式,可以通过这些模式对单个主机进行寻址.也可以同时对多个主机进行寻址或者寻址最近距离的主机. 单播寻址 在单播寻址方式 ...

  8. IEnumerator vs Iterator

    IEnumerator vs Iterator IEnumerator 不考虑Reset方法和原始列表的可变性,NoveNext和Current的只读向前用法: Initially, the enum ...

  9. Java二次开发海康SDK-对接门禁机

    写在最前 SDK版本:CH-HCNetSDKV6.1.6.45_build20210302_win64 参考文档:海康SDK使用手册_V6.1 对接测试设备型号:DS-K1T671M 设备序列号:E5 ...

  10. 3D-DNA 挂载染色体

    3D-DNA是一款简单,方便的处理Hi-C软件,可将contig提升到染色体水平.其githup网址:https://github.com/theaidenlab/3d-dna 3D-DNA流程简介 ...