【66期】Java容器面试题:谈谈你对 HashMap 的理解
回答
先判断有没有初始化
再判断传入的key 是否为空,为空保存在table[o] 位置
key 不为空就对key 进hash,hash 的结果再& 数组的长度就得到存储的位置
如果存储位置为空则创建节点,不为空就说明存在冲突
解决冲突HashMap 会先遍历链表,如果有相同的value 就更新旧值,否则构建节点添加到链表头
添加还要先判断存储的节点数量是否达到阈值,到达阈值要进行扩容
扩容扩2倍,是新建数组所以要先转移节点,转移时都重新计算存储位置,可能保持不变可能为旧容量+位置。
扩容结束后新插入的元素也得再hash 一遍才能插入。
先判断是否为空,为空就在table[0] 去找值
不为空也是先hash,&数组长度计算下标位置
再遍历找相同的key 返回值
Java 类库提供的Collections 工具包下的Collections.synchronizedMap()方法,返回一个线程安全的Map
或者使用并发包下的 ConcurrentHashMap,ConcurrentHashMap采用分段锁机制实现线程安全
使用HashTable (不推荐)
在hash 取下标时将1.7 的9次扰动(5次按位与和4次位运算)改为2次(一次按位与和一次位运算)
1.7 的底层节点为Entry,1.8 为node ,但是本质一样,都是Map.Entry 的实现
还有就是在存取数据时添加了关于树结构的遍历更新与添加操作,并采用了尾插法来避免环形链表的产生
但是并发丢失更新的问题依然存在。
考点分析
考点一:为什么初始容量必须为2 的幂?为什么负载因子为0.75f?为什么要做那么多扰动处理?
0/1 & 1 都为它本身
0/1 & 0 都为 0
考点二:& 字符虽然和 % 效果一样,但是操作效率更高
考点三:为什么int,String 适合为key?
考点四:并发操作导致的添加丢失和环形链表的产生过程
知识点拓展
拓展一:解决Hash 冲突的不同方案
链地址法
开发地址:线性探测法、平方探测法
完全散列:布谷鸟散列
拓展二:HashMap 是浅拷贝,说一说浅拷贝和深拷贝的区别
拓展三:说一说Collections.synchronizedMap()和HashTable 的区别
拓展四:说一说HashMap 如何实现有序(LinkHashMap 和TreeMap)以及他们的差别
拓展五:说一说ConcurrentHashMap 如何实现线程安全
结尾
相关文章