HashMap的底层原理面试必考题

为什么面试官如此青睐这道题?

HashMap里面涉及了很多的知识点,可以比较全面考察面试者的基本功,想要拿到一个好offer,这是一个迈不过的坎,接下来大发棋牌大发棋牌技巧技巧 我 用最通俗易懂的语言带着大家揭开HashMap的神秘面纱

一:HashMap的节点:HashMap是一个集合,键值对的集合,源码中每个节点用Node<K,V>表示

static class Node<K,V> implements Map.Entry<K,V> {
   final int hash;
   final K key;
   V value;
   Node<K,V> next;

Node是一个内部类,这里的key为键,value为值,next指向下一个元素,可以看出HashMap中的元素不是一个单纯的键值对,还包含下一个元素的引用

二:HashMap的数据结构:HashMap的数据结构为 数组+(链表或红黑树),上图:

image.png

为什么采用这种结构来存储元素呢?

数组的特点:查询效率高,插入,大发棋牌大发棋牌技巧技巧 删除 效率低

链表的特点:查询效率低,插入大发棋牌大发棋牌技巧技巧 删除 效率高

在HashMap底层使用数组加(链表或红黑树)的结构完美的解决了数组和链表的问题,使得查询和插入,大发棋牌大发棋牌技巧技巧 删除 的效率都很高。

三:HashMap存储元素的过程:

有这样一段代码:

HashMap<String,String> map = new HashMap<String,String>();
map.put("刘德华","张惠妹");
map.put("张学友","大S");

现在大发棋牌大发棋牌技巧技巧 我 要把键值对 “刘德华”,”张惠妹”存入map:

第一步:计算出键“刘德华”的hashcode,该值用来定位要将这个元素存放到数组中的什么位置.

什么是hashcode?

在Object类中有一个大发棋牌技巧方法 :

public native int hashCode();

该大发棋牌技巧方法 用native修饰,所以是一个大发棋牌大发棋牌技巧技巧 本地 大发棋牌技巧方法 ,所谓大发棋牌大发棋牌技巧技巧 本地 大发棋牌技巧方法 就是非java代码,这个代码通常用c或c++写成,在java中可以去调用它。

调用这个大发棋牌技巧方法 会生成一个int型的整数,大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 叫它哈希码,哈希码和调用它的对象大发棋牌大发棋牌技巧技巧 地址 和内容有关.

哈希码的特点是:

对于同一个对象如果没有被修改(使用equals比较返回true)那么无论何时它的hashcode值都是相同的

对于两个对象如果他们的equals返回false,那么他们的hashcode值也有可能相等

明白了hashcode大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 再来看元素如何通过hashcode定位到要存储在数组的哪里,通过hashcode值和数组长度取模大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 可以得到元素存储的下标。

刘德华的hashcode为20977295 数组长度为 16则要存储在数组索引为 20977295%16=1的地方

image.png

可以分两种情况:

1. 数组索引为1的地方是空的,这种情况很简单,直接将元素放进去就好了。

2. 已经有元素占据了索引为1的位置,这种情况下大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 需要判断一下该位置的元素和当前元素是否相等,使用equals来比较。

如果使用默认的规则是比较两个对象的大发棋牌大发棋牌技巧技巧 地址 。也就是两者需要是同一个对象才相等,当然大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 也可以重写equals大发棋牌技巧方法 来实现大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 自己的比较规则最常见的是通过比较属性值来判断是否相等。

如果两者相等则直接覆盖如果不等则在原元素下面使用链表的结构存储该元素

image.png

每个元素节点都有一个next属性指向下一个节点,这里由数组结构变成了数组+链表结构,红黑树又是怎么回事呢?

因为链表中元素太多的时候会影响查找效率,所以当链表的元素个数达到8的时候使用链表存储就转变成了使用红黑树存储,原因就是红黑树是平衡二叉树,在查找性能方面比链表要高.

四:HashMap中的两个重要的参数:HashMap中有两个重要的参数:初始容量大小和加载因子,初始容量大小是创建时给数组分配的容量大小,默认值为16,用数组容量大小乘以加载因子得到一个值,一旦数组中存储的元素个数超过该值就会调用rehash大发棋牌技巧方法 将数组容量增加到原来的两倍,专业术语叫做扩容.

在做扩容的时候会生成一个新的数组,原来的所有数据需要重新计算哈希码值重新分配到新的数组,所以扩容的操作非常消耗性能.

    image.png           

创建HashMap时大发棋牌大发棋牌技巧技巧 大发棋牌大发棋牌技巧技巧 我 们 可以通过合理的设置初始容量大小来达到尽量少的扩容的目的。加载因子也可以设置,但是除非特殊情况不建议设置.