HashMap底层原理剖析(面试收藏!!!)

HashMap底层原理剖析(超详细!!!)

一、散列表结构

散列表结构就是数组+链表的结构
在这里插入图片描述

二、什么是哈希?

Hash也称散列、哈希,对应的英文单词Hash,基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出

这个映射的规则就是对应的哈希算法,而原始数据映射后的二进制就是哈希值

不同的数据它对应的哈希码值是不一样的

哈希算法的效率非常高

三、HashMap原理讲解

3.1继承体系图

在这里插入图片描述

3.2Node数据结构分析
static class Node<K,V> implements Map.Entry<K,V> {
   
    final int hash;计算得到哈希值
    final K key;
    V value;
    Node<K,V> next;
}

interface Entry<K, V> {
   
        K getKey();
        V getValue();
        V setValue(V value);
3.3底层存储结构

在这里插入图片描述
当链表长度到达8时,升级成红黑树结构

3.4put数据原理分析

在这里插入图片描述
首先put进去一个key----value
根据key值会计算出一个hash值
经过扰动使数据更散列
构造出一个node对象
最后在通过路由算法得出一个对应的index

3.5什么是哈希碰撞?

在这里插入图片描述
当传入的数据key对应计算出的hash值的后四位和上一个一样时,这时候计算出的index就会一致,就会发生碰撞,导致数据变成链表
例如:
(16-1)------->0000 0000 0000 1111
“张三”------->0100 1101 0001 1011
“李四”-------->1011 1010 0010 1011

此时,就会发现,张三和李四计算出的hash值转化为二进制的后四位一致,导致计算出index一致

3.6JDK8为什么引入红黑树?

哈希碰撞,会带来链化,效率会变低

引入红黑树会提高查找效率

3.7扩容机制

每次扩容为初始容量的2倍

eg:16------->32

为了防止数据过多,导致线性查询,效率变低,扩容使得桶数变多,每条链上数据变少,查询更快

四、手撕源码

1.HashMap核心属性分析

树化阈值-----8和64

负载因子0.75

threshold扩容阈值,当哈希表中的元素超过阈值时,触发扩容

loadFactory负载因子0.75,去计算阈值 eg:16*0.75

size-------当前哈希表中元素个数

modCount--------当前哈希表结构修改次数

2.构造方法分析
public HashMap(int initialCapacity, float loadFactor) {
   
    //校验 小于0报错
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //capacity大于最大值取最大值
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    //负载因子不能小于等于0
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    //tableSizeFor方法
    this.threshold = tableSizeFor(initialCapacity);
}

---------------------------------------------------------
//传入一个初始容量,默认负载因子0.75
public HashMap(int initialCapacity) {
   
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
---------------------------------------------------------
//无参数,负载因子默认0.75
public HashMap() {
   
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
---------------------------------------------------------
//传入一个map的对象
public HashMap(Map<? extends K, ? extends V> m) {
   
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}
3.put方法分析
public V put(K key, V value) {
   
    //返回putVal方法,给key进行了一次rehash
    return putVal(hash(key), key, value, false, true);
}
----------------------------------------------------------
static final int hash(Object key) {
   
    //让key对应的hash值的高16位也参与运算
   int h;
   return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }
----------------------------------------------------------
final V putVal(int hash, K key, V value, boolean  onlyIfAbsent,boolean evict) 
{
   
    //tab:引用当前HashMap的散列表
    //p:表示当前散列表的元素
    //n:表示散列表数组的长度
    //i:表示路由寻址的结果
    Node<K,V>[] tab; Node<K,V> p; int n, i;
----------------------------------------------------------   	//延迟初始化逻辑,当第一次调用putVal的时候,才去初始化HashMap对象的散列表大小
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
------------------------------------------<
  • 32
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值