为什么不管大厂还是小厂,面试总是要提到 HashMap?( 二 )


扩容过程 , 我们就需要涉及以下几个核心参数:

  • 扩展因子:loadFactor
  • 实际元素数量:size
  • 数组长度:capacity
  • 扩容的阈值大小:threshold=capacity*loadFactor
当 HashMap 中元素数量大于 threshold , HashMap 就会开始扩容 。
JDK1.7 扩容的时候 , HashMap 每个元素将会重新计算 Hash 值 , 然后使用寻址算法 , 查找新的位置 。
为什么不管大厂还是小厂,面试总是要提到 HashMap?文章插图
在 JDk1.8 中 , 采用了一种更精妙的算法:
为什么不管大厂还是小厂,面试总是要提到 HashMap?文章插图
其使用 e.hash--tt-darkmode-color: #9B9B9B;">这里解释起来比较复杂 , 这里阿粉就不再详细展开 , 感兴趣同学可以自行查找一下相关文章 。
面试题问:加载因子为什么 0.75 , 而不是其他值?
答:可以说是一个经过考量的经验值 。 加载因子涉及扩容 , 下次扩容的阈值=数组桶的大小*加载因子 , 如果加载因子太小 , 这就会导致阈值太小 , 这就会导致比较容易发生扩容 。
如果加载因子太大 , 那就会导致阈值太大 , 可能冲突会很多 , 导致查找效率下降 。
多线程并发好了 , 终于到到了最后一个知识点 , 多线程并发 。
HashMap 在多线程并发情况下会怎么样?
这里我们需要分 JDK1.7 与 JDK1.8 来讲 。
在 JDK1.7 中 , 由于扩容迁移时采用了头插法 , 从而将会导致产生死链 。
void transfer(Entry[] newTable, boolean rehash) {int newCapacity = newTable.length;for (Entry e : table) {while(null != e) {Entry next = e.next;if (rehash) {e.hash = null == e.key ? 0 : hash(e.key);}int i = indexFor(e.hash, newCapacity);// 以下代码导致死链的产生e.next = newTable[i];// 插入到链表头结点 ,newTable[i] = e;e = next;}}}
为什么不管大厂还是小厂,面试总是要提到 HashMap?文章插图
而一旦产生死链 , 极有可能导致程序陷入死循环 , 从而导致 CPU 使用率上升 。
JDK1.8 中使用尾插法 , 从而解决这个问题 , 但是依然还会存在相关问题 。
比如:
并发赋值时被覆盖
为什么不管大厂还是小厂,面试总是要提到 HashMap?文章插图
并发的情况下 , 一个线程的赋值可能被另一个线程覆盖 , 这就导致对象的丢失 。
size 计算问题
为什么不管大厂还是小厂,面试总是要提到 HashMap?文章插图
img
每次元素增加完成之后 , size 将会加 1 。 这里采用 ++i方法 , 天然的并发不安全 。
面试题关于并发 , 这里可以提到很多面试题 。
可以是线程相关的 , 也可以是并发编程相关 。
不过如果面试官既然已经提到这里 , 我们可以试着将他引导他如何解决 HashMap 并发编程的问题 , 从而我们下面开始回答出 ConcurrentHashMap 。
最后经过上面一顿分析 , 我们可以看到小小一个 HashMap 其实涉及到很多知识点 , 这些点拆开来讲就可以变成一道道面试题 。
另外这些点在平常编程的过程中也要特别注意 , 一不小心我们就会踩一个大坑 。
【为什么不管大厂还是小厂,面试总是要提到 HashMap?】今天这篇文章主要提及一下 , HashMap 涉及的知识点 , 所以阿粉没有过多深入的分析 , 这里感兴趣的同学可以在深入学习准备一下 。