1、什么是HashMap?
答:HashMap 根据hash实现的一种Map接口实现类。
2、HashMap的底层数据结构?
答:在1.8之前,HashMap的数据结构:数组+链表
在1.8之后,HashMap的数据结构:数组+链表+红黑树
3、HashMap的初始值?
答:capacity:代表table.length loadfactor:加载因子 threhold:扩容的门槛值
1)、DEFAULT_INITIAL_CAPACITY = 1<<4(初始容量)
2)、MAXIMUM_CAPACITY = 1<<30(最大容量)
3)、DEFAULT_LOAD_FACTOR = 0.75f(加载因子)
4)、TREEIFY_THRESHOLD = 8(链表转化为树门槛值)
5)、UNTREEIFY_THRESHOLD = 6(树转化为链表门槛值)
7)、loadFactor(加载因子)
8)、threshold(扩容的门槛值)
9)、size(map当前key-value的数目)
4、初始化HashMap?
答:
1)HashMap(int initialCapacity, float loadFactor)
约束条件:0<=initialCapacity<=MAXIMUM_CAPACITY, loadFactor>=0, threshold=tableSizeFor(initialCapacity)(tableSizeFor(initialCapacity)大于等于initialCapacity的2的最小整数次幂)
2)HashMap(int initialCapacity) => HashMap(initialCapacity, DEFAULT_LOAD_FACTOR)
约束条件:同上
3) HashMap()
约束条件:loadFactor = DEFAULT_LOAD_FACTOR
4)HashMap(Map<? extends K, ? extends V> m)
约束条件:loadFactor = DEFAULT_LOAD_FACTOR
5、put元素过程?
答:put<key, value> 允许空值
1)、通过hash函数,计算key的hash
2)、如果桶表为空,通过resize函数初始化桶表
3)、计算hash值在表中的位置:(table.length - 1) & hash, 并查看在桶表中该位置是否有值,若无,则直接在该位置创建新值 new Node<>(hash, key, value, null)
4)、若该位置有值则判断该值是否为TreeNode
5)、若不是TreeNode,则从头遍历链表,若 Node.key == key || key.equals(Node.key) 则对值进行替换,否则,在尾部进行插入,校验该桶的链表长度是否大于TREEIFY_THRESHOLD-1,若大于等于,则转化为树结构。
6)、若是ThreeNode,则插入红黑树中
7)、若size大于threshold,则通过resize进行扩容
6、get元素过程?
答:get<key> 允许空值
1)、通过hash函数,计算key的hash
2)、计算hash值在表中的位置:(table.length - 1) & hash, 并查看在桶表中该位置是否有值,若无,则返回空
3)、若该位置有值则判断该值是否为TreeNode
4)、若不是TreeNode,则从头遍历链表若 Node.key == key || key.equals(Node.key) 则返回,若查询不到,则返空
5)、若是ThreeNode,则从树中获取该值
7、remove元素过程?
答:remove<key>允许空值
1)、通过hash函数,计算key的hash
2)、假设桶表不为空,计算hash值在表中的位置:(table.length - 1) & hash, 并查看在桶表中该位置是否有值,若无返回空
3)、若该位置有值则判断该值是否为TreeNode
4)、若不是TreeNode,则从头遍历链表,若 Node.key == key || key.equals(Node.key) 则对值进行删除
5)、若是ThreeNode,则删除该值
NOTE:
1)、hash函数:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
2)、resize函数:
创建桶表:
(1)、没有初始化initialCapacity、loadFactor,则使用默认值初始化。
(2)、有初始值,则使用初始值初始化
(3)、创建桶表
扩容:
(1):capacity<<1且threhold<<1
(2):创建新桶表
(3):若节点为TreeNode,则通过TreeNode方式进行rehash
(4):若节点为Node,则重新进行rehash(注意:这里面用了一个技巧(e.hash & oldCap) == 0的还是放在原来的位置,其他的放在原来位置+oldCap,(e.hash & oldCap) == 0表示小于原来容量的,这个等式基于oldCap一定是2的整数次幂,并且事实上也是这样的。基于此举个例子,假设之前容量为16,则二级制表示为00010000,那么小于16的进行&操作,结果都为零)
死锁:java8的实现并不会发生死锁,因为死锁的出现主要是顺序不一致造成的。而java8的实现顺序是一致的。
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}