HashMap的实现原理
一、基本概念
1.1线性链表
对于链表的新增,删除等操作(在找到指定操作位置后),仅需处理结点间的引用即可,时间复杂度为O(1),而查找操作需要遍历链表逐一进行比对,复杂度为O(n)
1.2数组
采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查找等方式,可将查找复杂度提高为O(logn);对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度也为O(n)
1.3哈希表
哈希表(hash table)也叫散列表,相比上述几种数据结构,在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下,仅需一次定位即可完成,时间复杂度为O(1)。1
数据结构的物理存储结构只有两种:顺序存储结构和链式存储结构
由于数组根据下标一次就可以查找到,因此hash表的主干就是数组。我们只需要将对象的关键字(hash值)映射(哈希函数)到数组中就可以了,然后通过数组一次就可以查找到元素。
例如:对A和B的存储,通过A的哈希int ah=f(A),int bh=f(B),那么,A和B在哈希表中的存储位置就只 ah、bh位置。
1.4hash冲突
1 | 如果计算两个值的hash值相同怎么办呢?这种情况就是Hash冲突 |
答案是通过链表解决,最先存入的放入链表的尾部,后来的放入链表的头部;
二、实现原理
1 | 通过对第一节的学习,我们知道hashMap其实底层是数组加链表实现的,那么具体怎么实现的 |
hashMap中维护了一个2的n次方自动扩展的数组,数组的每一项是一个链表。
2.1原理
1、利用key的hashcode从新计算hash,得出当前对象元素在数组中的位置;
2、存储时如果出现hash相同的key,此时有两种情况:如果key的值相同,则覆盖原来的值,如果不同就放到链表的头部;1
如果是jdk1.8,当链表的长度>8个后,链表就会转化为红黑树
3、获取时也是先计算key的hash,然后找到数组下标,在进一步判断值是否相同,从而取到值;
2.2小结
- 扩容:按2的N次方扩展,当达到数组长度的0.75倍的时候就要扩容;
- 重置(resize):数组扩大后需要从新计算已有数据的位置,对性能有影响;
- 默认数组大小=16
三、总结
在创建hashMap时,在知道长度的情况下,建议设置默认长度,这样可以提高性能。
如你知道Map的长度=1000,根据hashMap长度达到75%就要扩容的,既是1000/0.75=1334,那么至少map的长度要设置为1334,又根据map的长度是根据2的N次方幂计算的,因此长度设置为1024最合适。

