本文主要讲以下几个问题
1.HashMap相比其他集合框架有什么优势?
2.散列表有什么特点?
3.什么是哈希?
4.HashMap的继承体系?
5.Node数据结构分析
6.HashMap底层存储结构
7.put()原理分析
8.什么是哈希碰撞?
9.JDK1.8为什么引入红黑树?
补充说明:上面的的问题比较偏入门部分,很多人抱怨HashMap源码看不懂,老话说饭得一口一口慢慢吃,先把上面这些整懂了,源码部分带大家手撕源码就很轻松了
HashMap相比其他集合框架有什么优势?
我们都了解ArrayList和LinkedLis两个集合框架,显而易见两者最大得区别在于一个查询数据快,一个增删数据快,虽然两者可以互相转化,但是实际开发里谁没事去转两个集合玩呢,麻烦不省事的事情可不干,那么有没有一个集合是查询数据快,增删数据又快的呢?
答案是肯定的,于是有了咱们现在的HashMap集合,但是这样一来自然而然的HashMap所需内存开销就比另外两个集合(ArrayList和LinkedList)要大的多,所以在HashMap中,new一个HashMap对象的时候,这个对象并不具备存储空间,而在你调用它的put()方法的时候才进行实例化对象。
ArrayList底层的结构是数组
LinkedList底层结构是链表
JDK1.8以后,HashMap底层结构是数组+链表+红黑树
什么是哈希?
那HashMap底层决定数据存放在数组中哪一位的是什么?
哈希的概念对许多初学者来说很不友好,显得有些难懂,描述的过程我会尽量口语化一些(比较重要,建议贯彻了哈希概念后再开始看源码部分)
概念:散列,哈希都称为Hash
原理:把任意长度的输入的数据,通过Hash算法变成固定长度的数据输出,这个映射的规则就是我们的Hash算法,而原始数据映射后二进制就是哈希值
特点:
1.每个字符计算得到的Hash值不能反向推出原始数据(不可逆)
比放数据1111111计算出来的哈希值是5555,但是不能通过哈希值是5555就反推出数据是1111111
简单理解成1+1=2,但是2不一定是1+1,也可以是0+2
也可以理解成计算的Hash值得出来的值是让你拿来计算存放在哪个数组下标的,不是拿来给你反推的
至于怎么计算,后面会详解
2.输入数据的微小不同会得到完全不一样的Hash值,相同的数据会得到相同的Hash值
举个例子: 比方数据1111111111计算出的哈希值是2222
数据1111111110计算出的哈希值可能就是3333了
当然这只是一个例子,从这个例子也能充分体现出哈希值得数值范围是要比原本的数值范围要小得多
3.哈希算法执行效率高效,就算数据庞大也能快速计算出哈希值
4.hash算法冲突概率比较小
由于Hash原理是将输入框架的值映射到Hash空间内,而Hash值得空间远小于输入空间
简单理解成一个大范围的数据量通过映射到Hash空间里,变成一个小范围的数据量,就好比十几位的数据量,映射后只会是10位内的数据,数据量大的情况下一定会出现数据碰撞,就是映射后的数据重复,这种碰撞我们叫做哈希碰撞
再举个例子,一百个人,二十个位置,让这些人选位置坐,结果肯定有人是共享一个位置的
这就是抽屉原理,势必会出现不同的人,位置相同的情况,换句话说就是不同的数据,hash值相同
抽屉原理补充:十个水果,装在九个抽屉,任你怎么放,都有一个抽屉要装两个水果以上
HashMap的继承体系?
虽然HashMap是继承了AbstractMap抽象类,在HashMap里这个抽象类被继承后基本上方法都被重写了
我们都知道HashMap是通过key-value键值对的方式存储数据,但是在读源码的过程中我们会发现有个Node<k,v> k,v分别指的是key,value,那么问题来了,什么是Node结构?
Node数据结构分析
K和V就是我们了解的key,value,就不过多解释了
通过这里我们了解到,存放在底层的数据其实会被封装成Node<K,V>的形式
(强调一下)
这里的hash是Key的hash值经过函数扰动得到的,目的是为了让hash值更加散列,如果不这么做的后果是什么,举个例子
根据前面讲到的抽屉原理,十个苹果放在九个抽屉,如果我很作,将十个苹果放在同一个抽屉里,其他八个抽屉空着,这种数据全部堆在一起,就叫不散列,处理数据的时候会很麻烦,正常的存放方式应该是尽量平均的将苹果放在每个抽屉中
底层存储结构? 什么是Hash碰撞? 什么是链化? JDK8为什么引入红黑树?
带着些个问题看下图
JDK1.8后HashMap底层结构是数组+链表+红黑树 发生哈希冲突的时候使用尾插法加入数据
JDK1.7HashMap是以数组+链表的结构存储数据 发送哈希冲突的时候使用头插法加入数据
哈希冲突所产生的问题: 当一个数组的下标上那条链表的数据非常多的时候,这时我们在通过get(key)方法取数据的时候就变得很麻烦,也就是我们常说的数据链化严重,时间复杂度会从O(1)—>O(n),但HashMap的集合就是为了缓解查询数据快和修改数据快的问题而产生的,为了解决数据链化的问题,这时底层数据就要变成红黑树结构了
补充说明:至于转换的阈值是多少,手撕源码部分会揭晓
put数据的原理分析?
路由寻址算法: (map.length-1)&node.hash
以上HashMap的入门篇就讲完了,下期开始讲源码部分,祝源码旅途愉快