1、概念
HashMap:存储数据采用的是哈希表结构,元素的存取顺序不能保持一致。由于要保证键的唯一、不重复,需要重写键的hashCode()、equals()方法。
其中,链表的节点存储的是一个Entry对象,每个Entry对象存储四个属性(hash,key,value,next)
2、JDK1.7和1.8的区别
jdk1.7
采用数据+链表结构
jdk1.8
采用数据+链表+红黑树结构
3、2的N次幂扩容
HashMap的扩容公式:initailCapatity * loadFactor = HashMap
其中initailCapacity是初始容量:默认值是16;
其中loadFactor是负载因子:默认值是0.75;
4、什么时候进行扩容
当hashMap中的元素个数超过数组大小×loadFactor时,就会进行数组扩容,例如:当数组初始容量为16,所以16×0.75=12,也就是说当元素个数超过12的时候,就扩大一倍。
5、 如何解决hash冲突
什么是hash冲突
当不同的元素放在了数组的同一位置时,后放入的元素会以链表的形式,插在前一个元素的尾部,这个时候我们称放生了hash冲突。
如何解决hash冲突
hash冲突并不能完全解决,但是可以减少,通过(n-1)&hash,来决定,元素放在数组的位置,其中,n为数组的长度。(n-1)可以降低hash冲突发生的概率!
6、jdk1.7HashMap扩容死循环问题
HashMap是一个线程不安全的容器,在最坏的情况下,所有元素都定位到同一个位置,形成一个长长的链表,这样get一个值时,最坏情况需要遍历所有节点,性能变成了O(n)。
JDK1.7中HashMap采用头插法拉链表,所谓头插法,即在每次都在链表头部(即桶中)插入最后添加的数据。
死循环问题只会出现在多线程的情况下。
假设在原来的链表中,A节点指向了B节点。
在线程1进行扩容时,由于使用了头插法,链表中B节点指向了A节点。
在线程2进行扩容时,由于使用了头插法,链表中A节点又指向了B节点。
在线程n进行扩容时,…
这就容易出现问题了…在并发扩容结束后,可能导致A节点指向了B节点,B节点指向了A节点,链表中便有了环!!!
导致的结果:CPU占用率100%
7、一致性哈希算法
8、与hashTable的区别
hash值不同
HashMap:重新计算hash值
HashTable:直接使用对象的hashCode
是否允许null值
HashTable:key和value都不允许出现null值
HashMap:null能够作为键,但是只能有一个,值可以有多个为null
是否线程安全
HashTable:线程安全
HashMap:线程不安全
继承的父类不同
HashTable:继承Dictionary类
HashMap:继承AbstractMap类