HashMap 原理解读

参考目录:

1. HashMap 散列初体验
2. 为什么HashMap 常用String 对象作key
3. HashMap 原理
4.自定义 hashCode()
5.HashMap 的性能因子

当我们需要获得map 中的value 时,需要根据key 查找对应的value,假设一个map 中有很多条数据,当我们对键进行查询时,键没有按照特定的顺序保存,那么只能使用简单的线性查询,而线性查询时最慢的查询方式。

在上一篇博文中我们提到了散列,散列的最大价值在于速度:散列使得查询可以快度进行。由于查询的效率在于对键的查询速度,因此解决方法就是保持键的排序状态。

散列的原理则是更进一步,它将键保存在某处,以便能够很快找到。存储一组元素的最快的数据结构就是数组,所以用它来表示键的信息(注意:这里是指键的信息[存的是对应的 entry],并不是键本身)。

但是由于数组本身受到保存数量的限制,就会有一个问题:我们希望在Map 中保存数量不确定的值,但是如果键的数量被数组限制了,那要该怎么办呢?

答案就是:数组不保存键本身,而是通过一个对象生成一个数字,将其作为数组的下标。这个数字就是散列码,由定义在Object 中的、且可能由你的类覆盖hashCode() 方法(在计算机科学术语中被称为散列函数)生成。

为了解决数组容量被固定的问题,不同的键可以产生不同的下标。也就是说可能会产生数组。因此多大就不重要了,任何键总能在数组中找到它的对应的位置。

于是查询一个值的过程就是先计算散列码,然后使用散列码查询数组。如果能保证没有冲突,那么就是一个完美的散列函数,但是这种情况只是特例。通常冲突由外部链接处理:数组并不保存值,而是保存值的list 。 然后对list 中的值使用equals() 方法进行红黑树查询(在jdk1.8 之前的时候是使用线性查询,所以jdk1.8 在对于HashMap 的查询速度进行了进一步提高)。因此在查询时并不是查询整个list 而是跳跃式的查询,只对少量的元素进行比较。这就是为什么HashMap 会如此之快的原因了。

附一张jdk1.8 之前HashMap 底层原理图:数字代表数组下标,所以当进行元素存储时先计算key 的散列码,根据散列码计算其对应的数组下标然后存到数组中,如果另一个元素的散列码经过计算其数组下标也为1(如图),那么原先的list 向后移动,刚进来的代替它的位置,并通过链表相连。查找方式与存储方式相同,这种方式也并不是完美的查询方式,当计算散列码后找到对应的数组下标,然后还是需要线性查找保存值的list。
HashMap 原理解读_第1张图片

在jdk1.8 之后,其数据结构就又发生了变化变成了数组+链表+红黑树。在jdk1.8 中规定当链表的长度大于8并且map 中的容量大于64 时,那么对应的链表就会变成红黑树,散列码相同时,通过equals() 方法判定value,根据value的大小进行红黑树存储与查找。这样效率就又提升了很多。
HashMap 原理解读_第2张图片

你可能感兴趣的:(Java,基础)