这篇文章是记录本身分析 Java 8 的 HashMap
源码时遇到的疑问和总结,在分析的过程当中笔者把遇到的问题都记录下来,而后逐一击破,若是有错误的地方,但愿读者能够指正,笔者感激涕零。java
The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created.git
initial capacity 指定了 HashMap 内部的 hash table 的初始化容量,能够经过构造函数指定,默认的初始化容量为 16github
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
复制代码
The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.数组
能够看到 load factor 是用于肯定 hash table 什么时候扩容的重要参数之一。安全
Iteration over collection views requires time proportional to the "capacity" of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.数据结构
An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets. app
查看源码描述函数
描述主要提到三个问题:性能
笔者通过一番测试,在 hash 均匀分布的前提下,数据量从 100 ~ 千万级,initial capacity 设置从 1 到千万不等,发现这个值的大小对迭代性能和 rehash 的影响不是很大(到千万级别才会有稍微明显的效率问题),缘由是扩容时,每次都会 double threshold,有效避免了高频率的扩容动做;对迭代性能的影响也很是低(10W数据,1亿初始容量,毫秒级别影响)。测试
默认 0.75 便可,过低可能会致使频繁的扩容,而且有可能致使 java.lang.OutOfMemoryError: Java heap space
When
putting data && (hashtable.length == 0 || entries.length >= threshold)
Then resize() 复制代码
触发 resize 的时候,会发生一次扩容,并随 rehash。
扩容时会重建 hash table,因为新 hash table 的容量 = 旧 hash table 容量左移一位,所以须要 rehash,以确保新的 hash 落在 [0, new_capacity) 区间内。
hash
落入 [0, capacity) 区间?(capacity - 1) & (hashcode ^ (hashcode >>> 16))
capacity 取值范围: 2^(N+) - 1,0 < (N+) < 31。
在 hashCode 均匀分布的前提下(Object.hashCode() 默认为每一个对象生成不一样的 hash code,具体原理能够看 java doc),hashcode ^ (hashcode >>> 16)
能够下降 hash 冲突的概率(相对于 (capacity - 1) & hashcode
),原理是混合原始哈希码的高位和低位,以此来加大低位的随机性;(capacity - 1) & new_hash 能够保证计算出来的 index 落入 [0, capacity)。
经过覆写 Object##hashCode()
方法便可自定义 hashCode,就能够模拟 hash 碰撞,例如随机 hashCode 或固定 hashCode。
Given
TREEIFY_THRESHOLD = 8
When
hashCount >= TREEIFY_THRESHOLD Then treeifyBin(bin) 复制代码
当出现同一个 hash 达到 8 次碰撞,就会从链表转换成红黑树。
hash table 本质上是一个数组 + 链表或红黑树的数据结构, hash table 经过创建 hash 到数据节点的映射关系,巧妙的达成 O(1) 的检索效率。其中每一个链表保存着 hash 冲突(key 不相等,hash 值相等)的数据项,默认状况下,当达到 8 个冲突时,链表就会转换成红黑树,转换以后还伴随着 O(n) -> O(log n) 的效率提高,不过这种状况出现的几率很低(在分散的 hash code 的前提下,相同的 hash 值产生 8 次冲突的几率仅为千万分之 6),不过能够经过人为模拟重现这种状况。
频繁的 hash 冲突是致使 HashMap 性能降低的罪魁祸首,当同一个 hash 值冲突达到 8 次及以上时, 此时 rbtree 可能须要常常经过左旋、右旋和着色来保持自身平衡,这个代价之大跟同一个 hash 值的冲突次数成正比,所以须要维护好重写的 hashCode 方法,使 hash code 尽量分散。
用于提升哈希冲突条件下的性能,同时去除了之前版本遗留下来的问题,具体能够看这里 openjdk.java.net/jeps/180
红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为"对称二叉B树",它现代的名字源于Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文。红黑树的结构复杂,但它的操做有着良好的最坏状况运行时间,而且在实践中高效:它能够在 O(log n)时间内完成查找,插入和删除,这里的 n 是树中元素的数目。
上面是维基百科中的定义,同时红黑树还有 5 个性质,红黑树是每一个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制通常要求之外,对于任何有效的红黑树增长了以下的额外要求:
时间敏感型程序。
Linux 内核和 epoll 系统调用实现中使用的彻底公平调度程序使用红黑树。
HashMap
经过这个机制确保迭代时,若是修改 map 的结构(增,删),一经发现则当即抛出 ConcurrentModificationException
,而不是等到将来的某个时刻再通知异常,能够经过这个机制来捕获程序的一些 BUG.
在 HashMap 之上包装多一层,而且使用 synchronized
同步锁锁定 HashMap 实例的全部公开接口,Collections#synchronizedMap
已经提供了这样一种实现。
经过阅读源码、查阅资料和动手验证,才知道 HashMap 的知识点那么多,不过都啃得差很少以后,就会以为知识点不多(应该还有遗漏掉的或者不严谨的状况),确实让本身学到了不少以前不知道的知识点。
在接下来的文章中,笔者会经过 TDD (测试驱动开发)来记录如何实现一个精简版的 HashMap,避免一看就会,一作就废的尴尬局面。