源码分析HashMap(超详细图文源码解析)

前言:

相信大家肯定有过此类的问题,有的东西用了半天,到底是干嘛的呢?会用了但是不知道为什么要用它,今天就来说说这个HashMap。

正文:

当我们需要存储数据的时候,我们已知的动态数组有哪些呢?

但是这些数组虽然能够自动扩容,但是必须在初始时刻指定初始容量。而对于那些在编译时无法确定具体的数量即动态增长的数据,就需要用到Java集合类了:ArrayList 和 LinkedList,还有 Vector等等......

对于ArrayList 和 LinkedList,还有 Vector它们都有一些缺点,要么插入删除速度慢、要么就是遍历速度慢。

那么有没有一种插入、删除、遍历都比较不错的集合类呢?于是 HashMap 就出现了。

HashMap 是一个散列表,它存储的是一组键值对(key-value)的集合,并实现快速的查找。

特点:

  • 为了实现快速查找,HashMap 选择了数组而不是链表。以利用数组的索引实现 O(1) 复杂度的查找效率。

  • 为了利用索引查找,HashMap 引入 Hash 算法, 将 key 映射成数组下标: key -> Index。

  • 引入 Hash 算法又导致了 Hash 冲突。为了解决 Hash 冲突,HashMap 采用链地址法,在冲突位置转为使用链表存储。

  • 链表存储过多的节点又导致了在链表上节点的查找性能的恶化。为了优化查找性能,HashMap 在链表长度超过 8 之后转而将链表转变成红黑树,以将 O(n) 复杂度的查找效率提升至 O(log n)。

底层实现:

JDK1.8之前:HashMap底层是数组+链表,也就是链表散列。HashMap通过key的hashCode经过扰动函数处理后得到hash值,然后通过(n-1)& hash判断当前元素存放的位置,若果但前位置存在元素的话,就判断该元素与要存入的元素hash值以及key是否相同,如果相同直接覆盖,不相同就通过拉链法解决冲突。

JDK1.8之后:当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

源码对比:

JDK 1.8

static final int hash(Object key) {
 int h;
 // key.hashCode():返回散列值也就是hashcode
 // ^ :按位异或
 // >>>:⽆符号右移,忽略符号位,空位都以0补⻬
 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }

 JDK 1.7

static int hash(int h) {
 // This function ensures that hashCodes that differ only by
 // constant multiples at each bit position have a bounded
 // number of collisions (approximately 8 at default load factor).
 h ^= (h >>> 20) ^ (h >>> 12);
 return h ^ (h >>> 7) ^ (h >>> 4);
}

底层数据结构:

                           JDK1.7

拉链法:将链表和数组相结合,为了解决Hash冲突问题。也就是说创建一个链表数组,数组中每一格就是一个链表,如果遇到hash冲突就将冲突的值加到链表中即可。

                          JDK1.8 

TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构,效率很低。

总结:

HashMap 存在的意义就是实现一种快速的查找并且插入、删除性能都不错的一种 K/V(key/value)数据结构。

推荐阅读:拓展资料

任何的限制,都是从内心开始的!

当你不再忍耐,不再克制,才会真正的成熟!

在做任何事情时,都要有坚定且清晰的目标,还要牢记目标!

            

       小编需要您的关注哦!您的赞是对我最大的鼓励!

猜你喜欢

转载自blog.csdn.net/l_mloveforever/article/details/111356670