花了三天时间来仔细阅读hashMap的源码,期间补了下很多数据结构的知识,刷了很多相关的面试题并进行了整理 git
1.谈一下HashMap的特性?github
1.HashMap存储键值对实现快速存取,容许为null。key值不可重复,若key值重复则覆盖。 面试
2.非同步,线程不安全。 算法
3.底层是hash表,不保证有序(好比插入的顺序)api
2.谈一下HashMap的底层原理是什么?数组
基于hashing的原理,jdk8后采用数组+链表+红黑树的数据结构。咱们经过put和get存储和获取对象。当咱们给put()方法传递键和值时,先对键作一个hashCode()的计算来获得它在bucket数组中的位置来存储Entry对象。当获取对象时,经过get获取到bucket的位置,再经过键对象的equals()方法找到正确的键值对,而后在返回值对象。安全
3.谈一下hashMap中put是如何实现的?数据结构
1.计算关于key的hashcode值(与Key.hashCode的高16位作异或运算) 函数
2.若是散列表为空时,调用resize()初始化散列表 post
3.若是没有发生碰撞,直接添加元素到散列表中去
4.若是发生了碰撞(hashCode值相同),进行三种判断
4.1:若key地址相同或者equals后内容相同,则替换旧值
4.2:若是是红黑树结构,就调用树的插入方法
4.3:链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入以后判断链表个数是否到达变成红黑树的阙值8;也能够遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。
5.若是桶满了大于阀值,则resize进行扩容
4.谈一下hashMap中何时须要进行扩容,扩容resize()又是如何实现的?
调用场景:
1.初始化数组table
2.当数组table的size达到阙值时即++size > load factor * capacity 时,也是在putVal函数中
实现过程:(细讲)
1.经过判断旧数组的容量是否大于0来判断数组是否初始化过
否:进行初始化
是,进行扩容,扩容成两倍(小于最大值的状况下),以后在进行将元素从新进行与运算复制到新的散列表中
归纳的讲:扩容须要从新分配一个新数组,新数组是老数组的2倍长,而后遍历整个老结构,把全部的元素挨个从新hash分配到新结构中去。
PS:可见底层数据结构用到了数组,到最后会由于容量问题都须要进行扩容操做
5.谈一下hashMap中get是如何实现的?
对key的hashCode进行hashing,与运算计算下标获取bucket位置,若是在桶的首位上就能够找到就直接返回,不然在树中找或者链表中遍历找,若是有hash冲突,则利用equals方法去遍历链表查找节点。
6.谈一下HashMap中hash函数是怎么实现的?还有哪些hash函数的实现方式?
对key的hashCode作hash操做,与高16位作异或运算
还有平方取中法,除留余数法,伪随机数法
7.为何不直接将key做为哈希值而是与高16位作异或运算?
由于数组位置的肯定用的是与运算,仅仅最后四位有效,设计者将key的哈希值与高16为作异或运算使得在作&运算肯定数组的插入位置时,此时的低位实际是高位与低位的结合,增长了随机性,减小了哈希碰撞的次数。
HashMap默认初始化长度为16,而且每次自动扩展或者是手动初始化容量时,必须是2的幂。
8.为何是16?为何必须是2的幂?若是输入值不是2的幂好比10会怎么样?
https://blog.csdn.net/sidihuo/article/details/78489820
https://blog.csdn.net/eaphyy/article/details/84386313
1.为了数据的均匀分布,减小哈希碰撞。由于肯定数组位置是用的位运算,若数据不是2的次幂则会增长哈希碰撞的次数和浪费数组空间。(PS:其实若不考虑效率,求余也能够就不用位运算了也不用长度必需为2的幂次)
2.输入数据若不是2的幂,HashMap经过一通位移运算和或运算获得的确定是2的幂次数,而且是离那个数最近的数字
9.谈一下当两个对象的hashCode相等时会怎么样?
会产生哈希碰撞,若key值相同则替换旧值,否则连接到链表后面,链表长度超过阙值8就转为红黑树存储
10.若是两个键的hashcode相同,你如何获取值对象?
HashCode相同,经过equals比较内容获取值对象
11."若是HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
超过阙值会进行扩容操做,归纳的讲就是扩容后的数组大小是原数组的2倍,将原来的元素从新hashing放入到新的散列表中去。
12.HashMap和HashTable的区别
相同点:都是存储key-value键值对的
不一样点:
13.请解释一下HashMap的参数loadFactor,它的做用是什么?
loadFactor表示HashMap的拥挤程度,影响hash操做到同一个数组位置的几率。默认loadFactor等于0.75,当HashMap里面容纳的元素已经达到HashMap数组长度的75%时,表示HashMap太挤了,须要扩容,在HashMap的构造器中能够定制loadFactor。
14.传统hashMap的缺点(为何引入红黑树?):
JDK 1.8 之前 HashMap 的实现是 数组+链表,即便哈希函数取得再好,也很难达到元素百分百均匀分布。当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就至关于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),彻底失去了它的优点。针对这种状况,JDK 1.8 中引入了 红黑树(查找时间复杂度为 O(logn))来优化这个问题。
15. 平时在使用HashMap时通常使用什么类型的元素做为Key?
选择Integer,String这种不可变的类型,像对String的一切操做都是新建一个String对象,对新的对象进行拼接分割等,这些类已经很规范的覆写了hashCode()以及equals()方法。做为不可变类天生是线程安全的,
源码解析阅读:
2. http://www.javashuo.com/article/p-csdeqcso-dm.html
3. http://www.javashuo.com/article/p-uqkohdtn-dg.html
4. https://blog.csdn.net/panweiwei1994/article/details/76555359#commentBox
5.
更多关于hashMap集合的面试题:
https://zhuanlan.zhihu.com/p/32355676
https://zhuanlan.zhihu.com/p/40760616
http://www.javashuo.com/article/p-qlgzvacj-z.html
https://www.jianshu.com/p/7af5bb1b57e2
https://baiqiantao.github.io/Java/%E9%9B%86%E5%90%88/3AFbAb/