【Java数据结构】Hashmap、Hashtable、ConcurrentHashMap源码阅读笔记
2016-04-09 20:01
489 查看
转载请注明原文地址:/article/8126631.html
我把Android重难点和读书笔记都整理在github上:https://github.com/miomin/AndroidDifficulty
如果你觉得对你有帮助的话,希望可以star/follow一下哟,我会持续保持更新。
引言:这几天在捣鼓Hashmap跟Hashtable的源码,其中关注的 比较多的就是他两计算在Entry[]数组中index的方法到底有什么区别。
Hashmap跟Hashtable的实现原理比较类似,借用一张其他地方偷来的图。
可以看到,都是采用外拉链的方式来实现元素存储,底层是数组+链表实现,原理都不说了,学过数据结构中hash冲突解决的同学应该都能理解。
实现的关键在于如何通过key来计算对应value应该存放到数组中的位置,下面具体来看看。
HashTable的做法:index = (hash & 0x7FFFFFFF) % tab.length; 这相当于直接将hash值对数组长度取模(跟0x7FFFFFFF做&操作是为了保证hashcode的值为正数)。HashTable中数组的初始size为11,每次扩容都按照newsize = oldsize*2+1来计算。通过取模来计算index的值,从概率上来讲,保证了节点在数组上分配比较均匀(形成尽量短的拉链,有利于提高查询效率),但是取模操作的消耗是比较大的。
HashMap的做法则非常巧妙,index = hash & (tab.length - 1)。这有什么精妙之处呢,首先,Hashmap要求数组的size为2的幂乘,比如16,32,64,仔细看,当数组大小为16的时候,tab.length-1=15,在内存中的表示是00001111,将hash值与00001111做&(位与)操作后,会将hash值除了后四位全部抹为0,只保留了后四位,这样的方式完成了跟index = (hash & 0x7FFFFFFF) % tab.length一样的效果,就是取模。但是位运算的效率比取模操作高得多,也就是说HashMap的index计算方式要比Hashtable快得多。(Hashmap的初始size是16,每次扩容按照newsize = oldsize*2)
写Android的小伙伴可以在Android Studio中打开HashTable的源码看看,Google很明显的改动了Hashtable的index计算方式,改成跟Hashmap一样啦!
下面说说这三个Map的其他区别
初始size为11,扩容:newsize = olesize*2+1
计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length;
初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
计算index方法:index = hash & (tab.length - 1)
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
我把Android重难点和读书笔记都整理在github上:https://github.com/miomin/AndroidDifficulty
如果你觉得对你有帮助的话,希望可以star/follow一下哟,我会持续保持更新。
引言:这几天在捣鼓Hashmap跟Hashtable的源码,其中关注的 比较多的就是他两计算在Entry[]数组中index的方法到底有什么区别。
Hashmap跟Hashtable的实现原理比较类似,借用一张其他地方偷来的图。
可以看到,都是采用外拉链的方式来实现元素存储,底层是数组+链表实现,原理都不说了,学过数据结构中hash冲突解决的同学应该都能理解。
实现的关键在于如何通过key来计算对应value应该存放到数组中的位置,下面具体来看看。
HashTable的做法:index = (hash & 0x7FFFFFFF) % tab.length; 这相当于直接将hash值对数组长度取模(跟0x7FFFFFFF做&操作是为了保证hashcode的值为正数)。HashTable中数组的初始size为11,每次扩容都按照newsize = oldsize*2+1来计算。通过取模来计算index的值,从概率上来讲,保证了节点在数组上分配比较均匀(形成尽量短的拉链,有利于提高查询效率),但是取模操作的消耗是比较大的。
HashMap的做法则非常巧妙,index = hash & (tab.length - 1)。这有什么精妙之处呢,首先,Hashmap要求数组的size为2的幂乘,比如16,32,64,仔细看,当数组大小为16的时候,tab.length-1=15,在内存中的表示是00001111,将hash值与00001111做&(位与)操作后,会将hash值除了后四位全部抹为0,只保留了后四位,这样的方式完成了跟index = (hash & 0x7FFFFFFF) % tab.length一样的效果,就是取模。但是位运算的效率比取模操作高得多,也就是说HashMap的index计算方式要比Hashtable快得多。(Hashmap的初始size是16,每次扩容按照newsize = oldsize*2)
写Android的小伙伴可以在Android Studio中打开HashTable的源码看看,Google很明显的改动了Hashtable的index计算方式,改成跟Hashmap一样啦!
下面说说这三个Map的其他区别
HashTable
底层数组+链表实现,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurretnHashMap做了相关优化。初始size为11,扩容:newsize = olesize*2+1
计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length;
HashMap
底层数组+链表实现,可以存储null键和null值,线程不安全初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
计算index方法:index = hash & (tab.length - 1)
ConcurrentHashMap
底层采用分段的数组+链表实现,线程安全通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
相关文章推荐
- 初级线段树 POJ3264
- codeforces 555c 数据结构(map 的使用)
- 数据结构与算法08 之堆
- 过河问题-狼羊人菜
- 数据结构第三章课后题
- leetcode 80 Remove Duplicates from Sorted Array II
- 数据结构-栈的顺序结构两种方式
- 数据结构第四章课后题
- 举例讲解C语言程序中对二叉树数据结构的各种遍历方式
- 【数据结构与算法】RMQ+ST及线段树
- 用2个栈实现队列
- HDU 3308 线段树之区间合并
- 数据结构-二叉树的前序、中序、后序、层次遍历
- MySQL索引背后的数据结构及算法原理
- 栈
- 重学数据结构系列之——图的遍历(广度优先搜索和深度优先搜索)
- 数据结构与算法(一)
- 探索HashMap实现原理及其在jdk8数据结构的改进
- 重学数据结构系列之——图的储存
- 时间复杂度和空间复杂度[数据结构]