Map源码解析

基本介绍

其实HashMap底层是个什么东西我们之前也讲过, 就是一个哈希桶(差不多可以看成一个数组), 然后每一个节点又连接着链表/红黑树之类的, 下面让我们看一看具体在源码上是怎样实现的:

常量及其它

-> static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 

//这个指代的是哈希桶的大小, 默认为16

-> static final float DEFAULT_LOAD_FACTOR = 0.75f;

//默认的负载因子 -> 0.75

-> static final int TREEIFY_THRESHOLD = 8;

//树化的条件之一, 就是指当一个节点上的链表长度大于8时(也不一定是8, 但跟8是有关的, 后面讲put的时候再说),就会从链表转换为红黑树

-> static final int MIN_TREEIFY_CAPACITY =  64;

//树化的条件之一, 就是指当哈希桶的大小如果大于64(数组的长度), 就会将链表转换成红黑树

-> static final int UNTREEIFY_THRESHOLD = 6;

//解树化的条件, 当红黑树的大小小于8时, 就会从红黑树转换回链表. 

-> static class Node<K, V> implements Map.Entry<K, V> {...

这段代码定义了一个静态内部类'Node', 该类实现了'Map.Entry<K, V>'接口, 表示哈希表中的一个节点, 用于存储键值对.

 构造方法

含指定容量, 负载因子的

     public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}

这个是提供了指定容量, 负载因子的, 玩家可以自行设置这两个参数. 需要注意的是在:

if(initialCapacity > MAXIMUM_CAPACITY) {}

这个中, MAXIMUM_CAPACITY是指1>>30, 也就是说当玩家设置的值大于这个值之后, 就会被设置为这个值(显而易见, 为了防止移除) 

含指定容量的

    public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}

这个很好理解, 就是当不给负载因子时, 就是默认的0.75, 然后通过this()调用上面的构造方法. 

俺一无所有

    public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}

这个就比较令人匪夷所思了, 它只有默认的负载因子(当然,还是0.75). 然后奇怪的是它居然没有给设置容量没事, 让子弹飞一会.

put方法

    public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}

让我们先来看一下里面的hash()方法.

    static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

这里采用了将key的hashCode()值与h右移 16位取异或得到哈希值.

这种位操作目的是将hashCode()返回的高位和低位进行混合, 增加哈希值的随机性和分布性, 从而减少哈希冲突的概率.

再来看一下putVal()方法.

     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

不难发现在4, 5行中, 

if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;

我们发现当这个传来的哈希标为空/或者大小为0时, 会让表调用resize().

这里是resize()中的部分内容:

Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;

 通过这个, 我们就可以得出结论, 在第一次put的时候, 哈希桶大小分配为了默认的newCap(其实是16).

继续分析putVal()方法中的剩余部分.

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

 在这里,  就是哈希表中插入新键值对的逻辑(就是哈希桶这个位置为空), 通过(n - 1) & hash的方式映射到数组索引i上, 这是因为 'n'应该是2的幂次方(即哈希表长度应该是2的幂次方), 才能保证哈希值均匀分布. 然后调用newNode完成了插入操作. 

p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);
break;

当需要插到链表/红黑树上时, 就需要通过尾插法插入, 在这里, 我们发现, 当链表长度大于 8 - 1时, 就会通过treeifyBin方法进行树化.  

那么到这里就讲完了(我讲的是比较爱考的), 其它部分如果感兴趣的话希望你们自己看看源码推理推理.

下了...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/794874.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何修改网页显示的小图标(Favicon)

目录 1. 使用自定义Favicon 2. 在vue中设置Favicon 3. 使用Vue插件设置Favicon 网页显示的小图标&#xff0c;通常称为Favicon&#xff08;Favorites Icon&#xff09;&#xff0c;是网站的标识之一&#xff0c;也是浏览器标签页上显示的图标。 1. 使用自定义Favicon 准备图…

Rust个人学习之单元测试

基础标记说明 Rust 是具备内嵌单元测试模块的。在 Rust 中&#xff0c;可以通过在源代码文件的顶部使用 #[test] 属性来标记一个函数作为测试函数。通常&#xff0c;这些测试函数位于与它们测试的源代码相同的文件中&#xff0c;但位于 mod tests 模块中。这是一个常用做法。 …

空和null是两回事

文章目录 前言 StringUtils1. 空&#xff08;empty&#xff09;&#xff1a;字符串&#xff1a;集合&#xff1a; 2. null&#xff1a;引用类型变量&#xff1a;基本类型变量&#xff1a; 3. isBlank总结&#xff1a; 前言 StringUtils 提示&#xff1a;这里可以添加本文要记录…

日志打印的学习之log4j2(二)进阶案例

日志级别简述&#xff1a; trace追踪&#xff0c;就是程序推进一下&#xff0c;可以写个trace输出debug调试&#xff0c;一般作为最低级别&#xff0c;trace基本不用。info输出重要的信息&#xff0c;使用较多warn警告&#xff0c;有些信息不是错误信息&#xff0c;但也要给程…

计算机网络——35什么是网络安全

什么是网络安全 机密性&#xff1a;只有发送方和预订的接收方能否理解传输的报文内容 发送方加密报文接收方解密报文 认证&#xff1a;发送方和接收方需要确认对方的身份报文完整性&#xff1a;发送方、接收方需要确认的报文在传输的过程中或者事后没有被改变访问控制和服务的…

如何通过C++身份证实名认证接口实现实名认证功能

线上平台使用身份核验过程是验证个人身份真实性的过程&#xff0c;对于大多数线上平台来说&#xff0c;自己去开发集成身份证实名认证接口需要耗费大量的人力、物力成本&#xff0c;对此&#xff0c;为助力有需要的企业快速实现实名认证的功能&#xff0c;翔云平台提供了身份证…

利用nginx-http-flv-module实现三种直播

目录 一、说明 二、目标 三、实现 四、直播地址 一、说明 此文在《流媒体服务器的搭建(支持hls)》《搭建nginx-http-flv-module直播系统》之后编写,很多详细内容需要参考它。 流媒体服务器的搭建(支持hls)

力扣爆刷第112天之CodeTop100五连刷46-50

力扣爆刷第112天之CodeTop100五连刷46-50 文章目录 力扣爆刷第112天之CodeTop100五连刷46-50一、148. 排序链表二、22. 括号生成三、70. 爬楼梯四、2. 两数相加五、165. 比较版本号 一、148. 排序链表 题目链接&#xff1a;https://leetcode.cn/problems/sort-list/descriptio…

数据结构——顺序表(C语言)

目录 一、顺序表概念 二、顺序表分类 1.静态顺序表 2.动态顺序表 三、顺序表的实现 1.顺序表的结构体定义 2. 顺序表初始化 3.顺序表销毁 4.顺序表的检验 5.顺序表打印 6.顺序表扩容 7.顺序表尾插与头插 8.尾删与头删 9.在pos处插入数据 10.在pos处删除数据 11.查找数据 …

端盒日记Day02

JS 本本本本本地存储 localStorage 作用&#xff1a;可以将数据永久存储在本地&#xff08;用户电脑&#xff09;&#xff0c;除非手动删除&#xff0c;否则关闭页面也会存在 特性&#xff1a;a.可多窗口&#xff08;页面&#xff09;共享&#xff08;同一浏览器可以共享&a…

若依:一个基于Spring Boot、Spring Security、JWT、Vue和Element的全部开源快速开发平台

若依后台管理系统&#xff1a;一个基于Spring Boot、Spring Security、JWT、Vue和Element的全部开源快速开发平台 一、引言 随着软件开发技术的发展&#xff0c;前后端分离的开发模式逐渐成为主流。这种模式能够提高开发效率&#xff0c;降低维护成本&#xff0c;使前后端工程…

C++实现单例模式

#include <iostream> class Singleton { private: static Singleton* instance; // 指向单例实例的指针 Singleton() {} // 私有构造函数 public: // 获取单例对象的唯一全局访问点 static Singleton* getInstance() { if (instance nullpt…

c# wpf template itemtemplate+ListBox

1.概要 2.代码 <Window x:Class"WpfApp2.Window7"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expression/blend/…

Kafka参数介绍

官网参数介绍:Apache KafkaApache Kafka: A Distributed Streaming Platform.https://kafka.apache.org/documentation/#configuration

开源大语言模型(LLM)汇总(持续更新中)

随着ChatGPT的火爆&#xff0c;越来越多人希望在本地运行一个大语言模型。为此我维护了这个开源大语言模型汇总&#xff0c;跟踪每天不发的大语言模型和精调语言模型。 我将根据个模型采用的基础大模型进行分类&#xff0c;每个大模型下列出各派生模型。 Alpaca (Stanford) 斯…

STM32CubeIDE基础学习-通用定时器中断实验

STM32CubeIDE基础学习-通用定时器中断实验 文章目录 STM32CubeIDE基础学习-通用定时器中断实验前言第1章 工程配置1.1 工程外设配置部分1.2 生成工程代码部分 第2章 代码编写第3章 实验现象总结 前言 生活中很多应用都有用到定时器功能、计时功能等。 定时器中断可以大大降低…

Win10文件夹共享(有密码的安全共享)(SMB协议共享)

前言 局域网内&#xff08;无安全问题&#xff0c;比如自己家里wifi&#xff09;无密码访问&#xff0c;参考之前的操作视频 【电脑文件全平台共享、播放器推荐】手机、电视、平板播放硬盘中的音、视频资源 下面讲解公共网络如办公室网络、咖啡厅网络等等环境下带密码的安全…

python爬虫———post请求方式(第十四天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

C++学习笔记(五)

临时对象与C11右值引用 右值&#xff1a;不可取地址的值是右值。左值&#xff1a;可以取地址就是左值。 -------------------------------------- 临时对象&#xff1a; ----------------- 临时对象的特性&#xff1a; -------------------- 系统不分配地址&#xff0c;在下一…

android wifi连接

记住密码&#xff0c;第二次登录不必输入。 如果使用其他方式&#xff0c;可不可以。其实就是自己选择wifi。 ******************** 我根本没办法站在更高的维度去思考整个项目&#xff0c;认知也达不到&#xff0c;我很多的事情都不知道&#xff08;信息不全&#xff09;&…