Java最新开发多年HashMap不知道?Java原理分析put、get、resize,缓存+一致性哈希+分布式面试题

最后

小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

开源分享:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】

程序员代码面试指南 IT名企算法与数据结构题目最优解

这是” 本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近200道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。 “刷”完本书后,你就是“题王”!

image.png

《TCP-IP协议组(第4版)》

本书是介绍TCP/IP协议族的经典图书的最新版本。本书自第1版出版以来,就广受读者欢迎。

本书最新版进行」护元,以体境计算机网络技不的最新发展,全书古有七大部分共30草和7个附录:第一部分介绍一些基本概念和基础底层技术:第二部分介绍网络层协议:第三部分介绍运输层协议;第四部分介绍应用层协议:第五部分介绍下一代协议,即IPv6协议:第六部分介绍网络安全问题:第七部分给出了7个附录。

image.png

Java开发手册(嵩山版)

这个不用多说了,阿里的开发手册,每次更新我都会看,这是8月初最新更新的**(嵩山版)**

image.png

MySQL 8从入门到精通

本书主要内容包括MySQL的安装与配置、数据库的创建、数据表的创建、数据类型和运算符、MySQL 函数、查询数据、数据表的操作(插入、更新与删除数据)、索引、存储过程和函数、视图、触发器、用户管理、数据备份与还原、MySQL 日志、性能优化、MySQL Repl ication、MySQL Workbench、 MySQL Utilities、 MySQL Proxy、PHP操作MySQL数据库和PDO数据库抽象类库等。最后通过3个综合案例的数据库设计,进步讲述 MySQL在实际工作中的应用。

image.png

Spring5高级编程(第5版)

本书涵盖Spring 5的所有内容,如果想要充分利用这一领先的企业级 Java应用程序开发框架的强大功能,本书是最全面的Spring参考和实用指南。

本书第5版涵盖核心的Spring及其与其他领先的Java技术(比如Hibemate JPA 2.Tls、Thymeleaf和WebSocket)的集成。本书的重点是介绍如何使用Java配置类、lambda 表达式、Spring Boot以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。

image.png

JAVA核心知识点+1000道 互联网Java工程师面试题

image.png

image.png

企业IT架构转型之道 阿里巴巴中台战略思想与架构实战

本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。

image.png

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

resize();
afterNodeInsertion(evict);
return null;
}

虽然写我加了注释,但是我还是简单说一下这个的逻辑吧
1.首先判断哈希表,是否存在,不存在的时候,通过resize进行创建
2.然后在通过索引算法计算哈希表上是否存在该数据,不存在就新增node节点存储,然后方法结束
3.如果目标索引上存在数据,则需要用equals方法判断key的内容,要是判断命中,就是替换value,方法结束
4.要是key也不一样,索引一样,那么就是哈希冲突,HashMap解决哈希冲突的策略就是遍历链表,找到最后一个空节点,存储值,就像我的图一样。灵魂画手有木有,很生动的表式了HashMap的数据结构
5.最后一步就是判断是否到扩容阀值,容量达到阀值后,进行一次扩容,按照2倍的规则进行扩容,因为要遵循哈希表的长度必须是2次幂的概念

好,put 告一断落,我们继续 get 吧

public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}

get方法,恩,好,很简单。hash一下key,然后通过getNode来获取节点,然后返回value,恩。get就讲完了,哈哈。开个玩笑。我们继续看getNode

final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//哈希表存在的情况下,根据hash获取链表的头,也就是first对象
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//检测第一个first是的hash和key的内容是否匹配,匹配就直接返回
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//链表的头部如果不是那就开始遍历整个链表,如果是红黑树节点,就用红黑树的方式遍历
//整个链表的遍历就是通过比对hash和equals来实现
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}

我们在整理一下,get方法比put要简单很多,核心逻辑就是取出来索引上的节点,然后挨个匹配hash和equals,直到找出节点。
那么get方法就搞定了

再来看一下resize吧。就是HashMap的扩容机制

final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
//检测旧容器,如果旧容器是空的,就代表不需要处理旧数据
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//保存扩容阀值
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 对阀值进行扩容更新,左移1位代表一次2次幂
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//如果哈希表是空的,这里会进行初始化扩容阀值,
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({“rawtypes”,“unchecked”})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
//处理旧数据,把旧数据挪到newTab内,newTab就是扩容后的新数组
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
//如果当前元素无链表,直接安置元素
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
//红黑树处理
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
//对链表的索引重新计算,如果还是0,那说明索引没变化
//如果hash的第5位等于1的情况下,那说明 hash & n - 1 得出来的索引已经发生变化了,变化规则就是 j + oldCap,就是索引内向后偏移16个位置
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}

resize方法的作用就是初始化容器,以及对容器做扩容操作,扩容规则就是double
扩容完了之后还有一个重要的操作就是会对链表上的元素重新排列

(e.hash & oldCap) == 0

在讲这个公式之前,我先做个铺垫

16的二进制是 0001 0000
32的二进制是 0010 0000
64的二进制是 0100 0000

我们知道HashMap每次扩容都是左移1位,其实就是2的m+1次幂,也就是说哈希表每次扩容都是 16、32、64…n
然后我们知道HashMap内的索引是 hash & n - 1,n代表哈希表的长度,当n=16的时候,就是hash & 0000 1111,其实就是hash的后四位,当扩容n变成32的时候,就是 hash & 0001 1111,就是后五位

我为啥要说这个,因为跟上边的 (e.hash & oldCap) == 0 有关,这里其实我们也可以用

假设我们的HashMap从16扩容都了32。
其实可以用 e.hash & newCap -1 的方式来重新计算索引,然后在重排链表,但是源码作者采用的是另外一种方式(其实我觉得性能上应该一样)作者采用的是直接比对 e.hash 的第五位(16长度是后四位,32长度是后五位)进行 0 1校验,如果为0那么就可以说明 (hash & n - 1)算出来的索引没有变化,还是当前位置。要是第五位校验为1,那么这里(hash & n - 1)的公式得出来的索引就是向数据后偏移了16(oldCap)位。

所以作者在这里定义了两个链表,
loHead低位表头,loTail低位表尾(靠近索引0)
hiHead高位表头,hiTail高位表尾(远离索引0)

然后对链表进行拆分,如果计算出来索引没有变化,那么还让他停留在这个链表上(拼接在loTail.next上)
如果计算索引发生了变化。那么数据就要放置在高位链表上(拼接在hiTail.next)上

最后来个灵魂配图,链表重排

重拍完成后的HashMap

总结

总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。

如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

mg-OoXXOpGZ-1715433216144)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值