手撕 JDK1.8 HashMap源码(上)
本文是对于HashMap源码的铺垫 并没有涉及到源码
源码篇:手撕 JDK1.8 HashMap源码(下)
基础入门
什么是哈希
核心理论:Hash也称散列、哈希,对应的英文都是Hash。基本原理就是把任意长度
的输入,通过Hash算法变为固定长度
输出。
这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串
就是哈希值。
Hash的特点:
-
从hash值不可以
反向推导
出原始的数据 -
输入数据的
微小变化
会得到完全不同的hash值,相同的数据会得到相同的值 -
哈希算法的执行效率要
高效
,长的文本也能快速的计算出哈希值 -
hash算法的冲突概率要小
由于hash的原理是将输入空间
的值映射成hash空间
内,而hash值的空间远小于输入的空间。
根据抽屉原理
,一定会存在不同的输入被映射成相同输出的情况。
抽屉原理:桌子上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们发现至少会有一个抽屉里面放不少于两个的苹果,这一现象就是我们所说的“抽屉原理”。
HashMap原理讲解
1. HashMap的继承体系
// HashMap继承AbstractMap
public class HashMap<K,V> extends AbstractMap<K,V>
// AbstractMap实现Map
public abstract class AbstractMap<K,V> implements Map<K,V>
2. Node数据结构分析
// put到Map里面的数据都会被封装为一个Node<K,V>存放到散列表当中
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // K.hash()经过一次扰动
final K key;
V value;
Node<K,V> next; // hash碰撞 链表的next指针
3. 底层存储结构介绍
jdk1.8
之前,由 数组+链表 组成。
jdk1.8
开始,由 数组+链表+红黑树 组成。
4. put数据原理分析
5. 什么是Hash碰撞
进行路由寻址之后计算的下标index
相同,即出现Hash碰撞
。
6. 什么是链化
由于Hash碰撞
,从而形成链表
。
7. jdk8为什么引入红黑树
链化非常严重,链表过长,查询时间复杂度由
O
(
1
)
→
O
(
N
)
O(1)→O(N)
O(1)→O(N),故引入红黑树
解决。
8. HashMap扩容原理
空间换时间
负载因子
0.75
负载因子指的是什么呢?当我们要存10000个数据时,每一个哈希桶都会存好几百个数据,那么每一个哈希桶的链表长度都很长,它的性能会变得很低,即使它会优化成红黑树;负载因子 0.75 的含义就是当我们所有的桶已经有 75% 的桶都存上数据时,会对桶的数量进行扩容,桶会变得更多,它的扩容大小就是原长度的 2 倍,默认是16,从16扩容成了32,当然取余操作也要变成%32。
经官方测试 负载因子的 0.75 就是最佳值,如果负载因子是0.5,那么你的哈希桶永远只能存一半,但是它的占用空间大,效率高;如果给到0.99,那么这个哈希桶空间浪费的特别少,但是找数据就会比较慢。
API文档中的叙述:
文章参考:b站小刘讲源码视频