HashMap
HashMap是一个采用哈希表的键值对集合,这里从如下几个问题解读HashMap。
-
HashMap工作原理?
-
如何做到fail-fast?可靠吗?
-
容量超过负载因子定义的容量,怎么处理?
-
capacity & load factor 设置参考因素?
-
HashMap为何线程不安全?
-
为什么容量必须为2的整数次幂?
HashMap工作原理
HashMap 是一个采用哈希表实现的键值对集合。put元素时,通过hash计算出下标,然后存储在合适位置的bucket中。get元素时,通过hash计算出下标,然后遍历桶中的链表获取元素。存储基于一个数组,然后数组的每一个元素都是链表。
如何做到fail-fast?可靠吗?
HashMap内部有一个modCount属性,累计每一次结构变更操作。当生成迭代器时,会记录当前的modCount值,此后每次迭代都会拿记录的值与当前值比较,有差异,则抛出ConcurrentModificationException。但是,这个属性没有volatile标记,也没有同步语义等。并发操作下不存在happens-before关系,故不一定可靠。
容量超过负载因子定义的容量,怎么处理?
会触发resize,对所有的元素做rehash,重新分布元素,并按照当前总容量的2倍扩容。扩容后,阈值等也会随之调整。
capacity & load factor设置参考因素?
hashmap基于hashing的原理,本质上是用空间换时间来提升搜索效率,这2个参数的设置参考因素也是基于这一点来进行。
1.capacity代表容量,过小容易导致过多的resize动作;过高容易浪费大量的存储空间以及提高检索时间。
2.load factor表示负载比例,比例过低,容量浪费大量的空间;比例过高,产生碰撞的几率更大,更多的碰撞会降低搜索的效率。
HashMap为何线程不安全?
- 并发做节点增删时,一个线程新增的节点,挂在另一个线程的删除节点之后,可能导致数据丢失;
- 并发做resize时,可能导致节点死循环;
为什么容量必须为2的整数次幂?
由hash值找到对应索引的方法如下:
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
首先,length为2的整数次幂的话,h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;
其次,length为2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性,而如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间。
因此,length取2的整数次幂,是为了使不同hash值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列。
-
HashTable
- 为何线程安全?
- 获取index相对HashMap如何?
为何线程安全?
HasTable相对HashMap,基本上对所有的公开方法都增加了synchronized关键字,相对HashMap多了线程安全特性。
获取index相对HashMap如何?
相比HashMap的获取index逻辑,性能要差一些。HashMap只是一个取模动作,HashTable是取模后再取余。
-
ConcurrentHashMap
ConcurrentHashMap默认并发级别是16,通过Segment来分块管理entry集合。每个Segment都持有一个重入锁,限制并发编辑操作。
相比HashTable并发时锁整个Map,ConcurrentHashMap是锁Segment,锁粒度变小,整体性能提升。
同时对迭代器友好,在并发迭代时,不会抛出ConcurrentModificationException异常。
-
TreeMap
基于红黑树实现。深入解读可参考:http://blog.csdn.net/chenssy/article/details/26668941
-
LinkedHashMap
LinkedHashMap继承HashMap,在HashMap的功能上叠加了有序性(插入顺序,访问顺序),元素链表支持双向等功能。
LinkedHashMap扩展了HashMap中的Entry,在其中增加了Entry<K, V> before, after属性。通过这2个属性,支持双向链表。同时独立于table数组外有一个Entry<K, V> header节点,通过这个来遍历整个双向链表。
有序性则通过accessOrder属性来控制。
-
一致性哈希
将桶数量设置为一个极大值,机器与数据视为节点都可以被映射到这一个近似无穷大的空间中。每个机器节点可以管理顺序条件下的一组空间,数据节点hash后落在这组空间内的,会交由机器节点管理。当机器增删时,会产生管理空间的分割与合并处理,顺带归属这些空间的数据节点会做迁移。这样可以做到有限规模的数据迁移。在增删节点时,因为hash计算的缘故,机器节点分摊的数据节点可能不平衡。为了尽量做到平衡性,引入虚拟节点。每一个真实机器节点会映射几个虚拟节点。出于维护平衡性,虚拟节点会尽量分摊数据节点,做到尽量均匀。
详细可参考:
http://blog.csdn.net/cywosp/article/details/23397179