前言:基于上篇《HashMap的实现原理(上)》,我们学会了什么是HashMap、为什么需要HashMap,以及HashMap的基本实现原理。即回答了三个问题:“What? Why? How? ”。 这篇,将继续讲解,什么是Hash冲突,为什么会出现Hash冲突和其解决方法。但在讲解的流程上,我们先解释Why,再进而得出What,最后再讲解How。
一、为什么会出现Hash冲突?(Why)
由上篇《HashMap的实现原理(上)》中的第三节部分得知,HashMap会通过hash算法来计算,某一条数据,应该存储在数组的某一个位置上,即通过hash算法来获取数组的下标。
在最优最好的情况下,不同的数据,通过一定的hash算法计算,总是能得出不同的下标。但是,我们无法避免会出现这样的情况:不同的数据,得出相同的下标,这也就是我们经常所说的,Hash冲突(What)。
二、如何解决Hash冲突?(How)
实际上,Hash并不是单一的数组结构,而是数组加链表的结构,即:
[链表0][链表1].... ...[链表n]
每一个数组的元素,都装有一个链表,就像一个桶一样,装了一层层的东西。所以,管每一个数组元素叫bucket,而不是element。当出现hash冲突的时候,冲突的数据,以链表的形式拼接起来。即:
{} 代表链表的节点,每一个节点,至少包括:hash、key、value。
[链表0] =》 内部 =》
{hash=0,key="test", value=1} ---下个节点--->{hash=0,key="abc", value=2}
由上面的代码可知,虽然key不一样,一个是test, 一个是abc。但基于不同的hash算法,有可能会计算出相同的hash值0。这样,这两个元素就是冲突的,但通过链表,把它们链表起来,都存放在数组下标为0的位置,就解决了这个冲突的问题。
**但是,基于链表查找的劣势,当链表的某一个节点,即某一只桶里面存的节点越多时,跟HashMap查找相关功能的效率,就会越慢。**所以,hash算法越好,出现冲突的概率就越低。
总结: 到此为止,我们已经学会了为什么会出现Hash冲突,以及如何解决Hash冲突。但这里面有两个点,需要你们自己深入去了解下,如果有兴趣的话:
1、Hash算法。
2、 HashMap源码里面final Node<K,V> getNode(int hash, Object key)方法的实现。