Map--02 ---HashMap的底层实现原理

Map

  • Map:双列数据,存储key-value对的数据 —类似于高中的函数:y = f(x)

在这里插入图片描述

在这里插入图片描述

HashMap的底层实现原理?以jdk7为例说明:

在这里插入图片描述

jdk8 相较于jdk7在底层实现方面的不同:

在这里插入图片描述

红黑树与链表的实际复杂度

  • 链表 O(N)
  • 红黑树 Olog(n)

HashMap存储结构

在这里插入图片描述
在这里插入图片描述

HashMap源码分析

JDK1.8

HashMap源码重要的常量

在这里插入图片描述

  • DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
  • DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
  • threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
  • TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

Node对象(内部类)

  • final int hash;
  • final K key;
  • V value;
  • Node<K,V> next;

(应用变量next,用于指向下一个元素,可以看做索引)

在这里插入图片描述

TreeNode

在这里插入图片描述

在这里插入图片描述

初始化构造方法

在这里插入图片描述

put 方法详解:

在这里插入图片描述

首先调用 hash方法

在这里插入图片描述

  • hashmap里面的hash方法 ,其中调用了Object;里面的hashcode方法,但并不完全由它决定
  • 得到hash值,相同的元素hash值一定相同.
  • 但hash值相等的两个元素,不一定相同,还得调用equals方法判断

putVal-方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 首先申明一个Node<K,V>[ ] tab ,NOde类型的数组,属性名 tab.
  2. 判断tab=tanle 是不是空,或者数组的长度是否为0
  3. 如果是的 调用resize()方法

resize()方法

  • 主要用于生成Node[ ]数组,默认初始容量16 ,加载因子0.75

在这里插入图片描述
在这里插入图片描述

通过hash值计算,得到在Node数组中的存放位置

  • i = (n - 1) & hash

在这里插入图片描述

计算机与&运算 比 模%运算更高效

  • HashMap的容量为什么是2的n次幂,和这个(n - 1) & hash的计算方法有着千丝万缕的关系,符号&是按位与的计算,这是位运算,计算机能直接运算,特别高效,按位与&的计算方法是,只有当对应位置的数据都为1时,运算结果也为1,当HashMap的容量是2的n次幂时,
  • (n-1)的2进制也就是1111111***111这样形式的,这样与添加元素的hash值进行位运算时,能够充分的散列,使得添加的元素均匀分布在HashMap的每个位置上,减少hash碰撞,下面举例进行说明。

案例:

在这里插入图片描述
上面四种情况我们可以看出,不同的hash值,和(n-1)进行位运算后,能够得出不同的值,使得添加的元素能够均匀分布在集合中不同的位置上,避免hash碰撞。

终上所述,

HashMap计算添加元素的位置时,使用的位运算,这是特别高效的运算;另外,HashMap的初始容量是2的n次幂,扩容也是2倍的形式进行扩容,是因为容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少hash碰撞,避免形成链表的结构,使得查询效率降低!

情况1

如果计算出来的此位置上的数据P为空,则此时的key1-value1添加成功。 ----情况1

如果计算出来的Node数组中的存放位置P是空值:则直接调用构造方法newNode添加元素

  1. p = tab[i = (n - 1) & hash]) == null
  2. tab[i] = newNode(hash, key, value, null);

在这里插入图片描述

- 如果此位置上的数据不为空

意味着此位置上存在一个或多个数据(以链表形式或者红黑树形式存在)),

  1. 需要判断这个哈希桶存放位置下面到底存在几个元素,用到for循环遍历

在这里插入图片描述

  • 此时再比较key1和已经存在的一个或多个数据的哈希值:

情况 2

如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。

  • (e = p.next) == null) 不存在key元素相等的情况,等于空

直接new 一个node对象存放进去

在这里插入图片描述

情况 3

key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同

  • p.hash == hash

则再调用key1所在类的equals(key2)方法,进行比较

在这里插入图片描述

哈希值相同且equals()返回true:那么此时使用value1替换value2

在这里插入图片描述

在这里插入图片描述

情况 4

如果哈希值相同且equals()返回false

在这里插入图片描述

如果e = p.next) == null,则代表这个哈希桶位置下面所有的元素都遍历完了.且不存在相同的元素key1

那么就直接new 一个node对象存放进去

在这里插入图片描述

  • 这个Nde对象排在这个链表的最后面
  • 这个Nde对象中next属性为null,

(jdk1.8是尾插法,1.7是头插法)
头插法可能在高并发的情况下造成死锁

在这里插入图片描述

如果遍历中的元素有哈希值相同且equals()返回true,则break.然后执行和情况3一样的代码.

  • 使用value1替换value2

在这里插入图片描述

分析:

在这里插入图片描述
在这里插入图片描述

以上源码分析 ,没有涉及到 如果已转化为红黑树,putTreeVal方法的分析

在这里插入图片描述
在这里插入图片描述

以上源码分析 ,没有涉及到链表如何转化为红黑树的代码treeifyBin

treeifyBin

在这里插入图片描述
在这里插入图片描述
treeifyBin
在这里插入图片描述

  1. 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8
  2. 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储

以上源码分析 ,没有涉及到数组扩容的 resize()方法

resize()
在这里插入图片描述
在这里插入图片描述

总结:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值