一、HashMap 底层原理
-
HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的 key、value 都可以为 null,此外,HashMap 中的映射不是有序的。
-
jdk1.8 之前 HashMap 由 数组 + 链表 组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突(两个对象调用的 hashCode 方法计算的哈希值一致导致计算的教组索引值相同)而存在的(“拉链法”解决冲突)。jdk1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(或者红黑树的边界值,默认为 8 )并且当前数组的长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。
-
补充:将链表转换成红黑树前会判断,即便阈值大于8,但是数组长度小于64,此时并不会将链表变为红黑树,而是选择逬行数组扩容。
这样做的目的是因为数组比较小,尽量避开红黑树结构,这种情况下变为红黑树结构,反而会降低效率,因为红黑树需要逬行左旋,右旋,变色这些操作来保持平衡。同时数组长度小于64时,搜索时间相对要快些。所以结上所述为了提高性能和减少搜索时间,底层阈值大于8并且数组长度大于64时,链表才转换为红黑树,具体可以参考 treeifyBin() 方法。
当然虽然增了红黑树作为底层数据结构,结构变得复杂了,但是阈值大于8并且数组长度大于64时,链表转换为红黑树时,效率也变的更高效。
-
小结:
HashMap 特点:
-
存储无序的。
-
键和值位置都可以是 null,但是键位置只能存在一个 null。
-
键位置是唯一的,是底层的数据结构控制的。
-
jdk1.8 前数据结构是链表+数组,jdk1.8 之后是链表+数组+红黑树。
-
阈值(边界值)> 8 并且数组长度大于 64,才将链表转换为红黑树,变为红黑树的目的是为了高效的查询。
-
1.1 hashmap和hashtable的区别
HashMap | HashTable | 说明 |
---|---|---|
无序的 | 无序的 | |
HashMap 继承了AbstractMap 抽象类 | HashTable 继承了Dictionary 抽象类 | 父类不同 |
HashMap 允许一个空键(其他的空键会覆盖第一个空键)和 任意数量 的NULL 值 | HashTable *不允许NULL值(包括键或值) | <key, value> 的NULL 区别 |
方法是异步的(即,线程不安全,效率较高) | 方法是同步的(即,线程安全的,效率较低) | HashTable 和HashMap 最主要的区别。 如果对同步性没有要求,建议使用HashMap 。 看HashTable 的源码(,除构造函数 外,HashTable 的所有 public 方法声明中都有 synchronized 关键字,而HashMap 的源码中则没有。 |
HashMap 去掉了HashTable 的contains() 方法,但是加上了containsValue() 和containsKey() 方法 | ||
HashMap 中hash数组 的默认大小是16 ,而且一定是2的指数 | HashTable 中hash数组 默认大小是11 ,增加的方式是 old * 2 + 1 |
相同点
HashMap & HashTable |
---|
HashMap 和HashTable 的底层实现都是数组 + 链表 实现 |
HashMap 和HashTable 的数据元素是无序的 |
为了成功地在HashMap 和HashTable 中存储和获取对象,用作key 的对象必须实现hashCode() 方法和equals() 方法 |