HashMap的tableSizeFor解析
我们都知道,对于HashMap来说,数组的容量为2的倍数,但是我们可以在创建map的时候传入一个数组的大小
此时,这个初始化数组大小会传给一个双参的构造器
1. 创建HashMap
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>(10);
}
2. 构造器
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
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;
//阈值(HashMap中最多能容纳的元素个数)
// 阈值 = 数组长度 * 负载因子
// 但是注意,刚初始化的时候阈值用来临时保存用户指定的初始容量或者默认初始容量16
// 只有在第一次inflateTable()扩容之后,才会用计算出的正确的容量乘以负载因子赋值
//此时传入的initialCapacity是10,会调用tableSizeFor方法,将其转为大于等于它的一个2的幂次数,例如7转为8,8转为8,10就转为16
this.threshold = tableSizeFor(initialCapacity);
}
3. tableSizeFor具体实现
static final int tableSizeFor(int cap) {
//首先减一操作是为了如果本身就为2的幂次数(8,16)的话,就使其降为一个小于2的幂次的数
//例如我们传入的10,对应的2进制为00000000 00000000 00000000 00001010
int n = cap - 1;
//减后
//00000000 00000000 00000000 00001001
n |= n >>> 1;
//右移后
//00000000 00000000 00000000 00000100
//之后再与自身相或也就是上面两个二进制相或
//00000000 00000000 00000000 00001101
n |= n >>> 2;
//再次右移两位
//00000000 00000000 00000000 00000011
//再或
//00000000 00000000 00000000 00001111
//此时我们可以看到我们想要的效果了
//将低位全部置为1!
//后面这些也是一样的操作,因为int是32位的
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
//之后对 与完的数字进行+1操作,(这些判断我就不说了)
//00000000 00000000 00000000 00010000
//此时+1后变为了一个2的幂次
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
4. 接下来我在说两个别的例子
-首先是32,他其实最后的结果要的就是32,对应的二进制是
00000000 00000000 00000000 00100000
-接下来先减一
00000000 00000000 00000000 0001 1111
-此时其实我们不用看了,右移之后或上任何数字,此时最后5位是不会变的,都是5个1
00000000 00000000 00000000 0001 1111
-之后对其进行+1操作,就变回了我们的32
00000000 00000000 00000000 0010 0000
-在接下来是很大的数字,这个数字具体是多少我也没算过。。。也不知道,最大容量MAXIMUM_CAPACITY = 1 << 30,所以应该是没超的
注意观察最高位
0010 0000 0010 0100 0101 1000 0010 0101
-先减一
0010 0000 0010 0100 0101 1000 0010 0100
-在右移1位
0001 0000 0001 0010 0010 1100 0001 0010
-此时上面两个相或
0011 0000 0011 0110 0111 1100 0011 0110
--此时再右移两位
啊!不想右移了,我们看一下结果吧
我们观察一下上面一堆,可以发现我们的结果其实与后面几位无关,只与最高的那个1位有关
-例如我们最近的这个
0010 0000 0010 0100 0101 1000 0010 0101
-我们其实可以省略除了最高的那个1之外的数字,我们将他们全部忽略,使用*代替,表示我们不关心它
001* **** **** **** **** **** **** ****
-之后右移一位
0001 ****
-相或
0011 ****
-之后右移两位
0000 11**
-相或
0011 11**
-右移四位
0000 0011 11** **
-相或
0011 1111 11** **
通过不断的这样的操作,将最高位1之后位全部置为1
之后在+1就可以求出来一个2的幂次了
5. 那有些朋友可能会问了,开始为什么要减一呢?
因为对于以下数字,我们可以看出来,减一实际上并没有什么影响,甚至你减个88都没什么影响,那为什么减呢?
0010 0000 0010 0100 0101 1000 0010 0101
主要是为了解决2的幂次的存在
因为对于2的幂次来说,例如32
00000000 00000000 00000000 00100000
我们如果直接去计算,直接右移取或,右移取或
那么我们得到的结果是
00000000 00000000 00000000 00111111
此时最终+1取2的幂的话结果为64!但是我们要的是32,所以说要先减1,使其成为一个比2的幂次小的数字
00000000 00000000 00000000 01000000
HashMap的tableSizeFor解析的更多相关文章
- JDK8 HashMap 源码解析
HashMap中数据结构 在jdk1.7中,HashMap采用数组+链表(拉链法).因为数组是一组连续的内存空间,易查询,不易增删,而链表是不连续的内存空间,通过节点相互连接,易删除,不易查询.Has ...
- HashMap源码解析 非原创
Stack过时的类,使用Deque重新实现. HashCode和equals的关系 HashCode为hash码,用于散列数组中的存储时HashMap进行散列映射. equals方法适用于比较两个对象 ...
- Java中的容器(集合)之HashMap源码解析
1.HashMap源码解析(JDK8) 基础原理: 对比上一篇<Java中的容器(集合)之ArrayList源码解析>而言,本篇只解析HashMap常用的核心方法的源码. HashMap是 ...
- 最全的HashMap源码解析!
HashMap源码解析 HashMap采用键值对形式的存储结构,每个key对应唯一的value,查询和修改的速度很快,能到到O(1)的平均复杂度.他是非线程安全的,且不能保证元素的存储顺序. 他的关系 ...
- 【转】Java HashMap 源码解析(好文章)
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...
- 史上最全HashMap红黑树解析
HashMap红黑树解析 红黑树介绍 TreeNode结构 树化的过程 红黑树的左旋和右旋 TreeNode的左旋和右旋 红黑树的插入 TreeNode的插入 红黑树的删除 TreeNode的删除节点 ...
- HashMap源码解析和设计解读
HashMap源码解析 想要理解HashMap底层数据的存储形式,底层原理,最好的形式就是读它的源码,但是说实话,源码的注释说明全是英文,英文不是非常好的朋友读起来真的非常吃力,我基本上看了差不多 ...
- 详解HashMap源码解析(下)
上文详解HashMap源码解析(上)介绍了HashMap整体介绍了一下数据结构,主要属性字段,获取数组的索引下标,以及几个构造方法.本文重点讲解元素的添加.查找.扩容等主要方法. 添加元素 put(K ...
- 【Java深入研究】9、HashMap源码解析(jdk 1.8)
一.HashMap概述 HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现.与HashTable主要区别为不支持同步和允许null作为key和value.由于HashMap不是线程 ...
随机推荐
- 使用dom4j工具:xml总结
1.IO流:BufferedReader字符流readLine();截取 不可行!!!2.XML解析:dom4j查:标签:element("name")elements(" ...
- Linkerd 2.10(Step by Step)—控制平面调试端点
Linkerd 2.10 系列 快速上手 Linkerd v2 Service Mesh(服务网格) 腾讯云 K8S 集群实战 Service Mesh-Linkerd2 & Traefik2 ...
- centos7 Tomcat 停止服务时报错: java.net.ConnectException: 拒绝连接 (Connection refused)
2021-08-02 1.问题描述 配置完 server.xml 文件后,执行 shutdown.sh 脚本停止服务,出现以下错误 2. 解决方法 查看当前正在运行的 Java 进程号 # 找到 Ja ...
- MySQL——MySQL初始化配置文件
初始化配置文件(影响服务器和客户端程序) 1.MySQL初始化配置加载顺序: 命令行 ----> 初始化配置文件 ----> 预编译选项 2.MySQL初始化配置文件加载顺序: (1)/e ...
- 2021 年 9 月 TIOBE 指数 C# 增长突破 1.2%
TIOBE 编程社区指数是编程语言流行程度的指标.该指数每月更新一次.评级基于全球熟练工程师.课程和第三方供应商的数量.谷歌.必应.雅虎.维基百科.亚马逊.YouTube 和百度等流行搜索引擎用于计算 ...
- appnium显式等待机制
强制等待: sleep不推荐全局隐式等待 全局隐式等待: 在服务端等待 driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); ...
- Emit优化反射(属性的设置与获取)
在频繁的通过反射来设置和获取属性的值时是比较耗时的,本章通过Emit技术优化反射来提高获取和设置属性值的效率 一.实现代码: /// <summary> /// 设置器委托 /// < ...
- UVA 1599 Ideal Path(双向bfs+字典序+非简单图的最短路+队列判重)
https://vjudge.net/problem/UVA-1599 给一个n个点m条边(2<=n<=100000,1<=m<=200000)的无向图,每条边上都涂有一种颜色 ...
- jquery动态生成dom(比如append)导致js事件无效
如果无效用这个方法: on() 方法在被选元素及子元素上添加一个或多个事件处理程序. <div id="zkdiv"> <input type="bu ...
- TP5增加扩展配置目录
ThinkPHP5.0.1版本开始增加了扩展配置目录的概念,在应用配置目录或者模块配置目录下面增加extra子目录,下面的配置文件都会自动加载,无需任何配置. 这极大的方便了我们进行扩展配置,比如在a ...