自己正在准备秋招,总结下面试常遇到的关于HashMap的问题
一、如何解决哈希冲突
- 解决哈希冲突的办法有开放地址法和链地址法
- 开放地址法:线性探测、二次线性探测、再哈希
- 链地址法:插入时间复杂度最优o(1),最差o(n)
二、HashMap中常见的问题
-
数组的长度总是2的n次方,为什么?
length总是2的n次方时,hash & (length-1)运算等价于hash % length , 但是&比%具有更高的效率。比如 n % 32 = n & (32 -1)
static final int hash(Object key) { int h; // 对象的hashCode的高16位亦或低16位 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } // 用&代替了%,提高了效率 // 计算插入位置 i = (table.length - 1) & hash;
-
Map的size和length的区别?
size是key-value个数,length是数组长度
-
默认的负载因子?实时负载因子是如何计算的?
默认为0.75,实时负载因子计算方式为size(key-value个数) / length(数组长度)
-
HashMap的put方法?
4.1 如果是第一次put,hashMap内的数组还没初始化,先进行初始化
4.2 利用hash函数得到值 hash & (length - 1) 得到插入的位置
4.3 如果插入的位置为空,构建新节点直接插入,执行4.6
4.4 如果插入的位置不为空,但是key值重复,进行覆盖, 执行4.6;如果key值不重复,执行4.5
4.5 判断该链是树还是普通链表,如果是树直接在树中插入;如果是链表则遍历,如果链表的长度大于8,转为树,否则遍历插入(重复的key覆盖)
4.6 插入成功后,判断实际存在的键值对数量size是否超过了最大容量threshold,如果超过,进行扩容
4.7 如果新插入的key不存在,则返回null,如果新插入的key存在,则返回原key对应的value值
-
HashMap初始容量是多少?扩容容量是多少?怎么扩容?
初始容量为16,扩容后为旧容量的两倍,计算新桶数组的容量 newCap 和新阀值 newThr,然后将原集合的元素重新映射到新集合中
-
遍历HashMap的方法
6.1
//1、遍历keySet for(String key : map.keySet()){ System.out.println(map.get(key)); }
6.2
Set<Map.Entry<String,Object>> entrySet = map.entrySet(); // 遍历enrtySet for(Map.Entry<String,Object> entry : entrySet){ System.out.println(entry.getKey()+"-"+entry.getValue()); }
6.3
Iterator<Map.Entry<String,Object>> iterator = map.entrySet().iterator(); // 利用迭代器,因为entrySet本质上是个Set集合 while(iterator.hasNext()){ Map.Entry<String,Object> mapEntry = iterator.next(); System.out.println(mapEntry.getKey()+"-"+mapEntry.getValue()); }