hashMap tableSizeFor 实现原理
基于jdk1.8
hashMap实现,要求容量大小是2的整次方,例如:2/4/8/16/32/64/128...,而不能是中间的某个值。这是为什么呢?
map是数组+链表的数据结构,读写数据都需要首先获取数组中的下标值,获取的方式是通过hashcode取余。取余so easy,我们都会,假定运算后的hashcode=17,容量大小capacity=16,17%16=1,很容易得出元素落在数组的下标[1]内。
但是还有一种方式可以获取到正确的下标值,17 &(16-1)=1。
17二进制: 10001
15二进制: 01111
17&15: 00001 = 1
计算机的物理特性,决定位运算才是取余的正确打开方式。但是却有一个限制,被位与的数值有效位必须全部都是1,如15:1111可以,13:1101则不行。
这里位与运算得到的是下标位置,数组容量要在最大下标值的基础上加1,等价于二进制中被位与的数值进位,如15进1位=16:10000,2的4次方。
如果我们new HashMap<>(13),输入一个非整次方的数值,hashmap会自动调整成最近的整次方,例如这里的13最终会转换成16,实现方法为:java.util.HashMap#tableSizeFor
怎么实现呢?
2的整次方的特性是二进制有效位只有一个1,退位后当前1消失,后面bit位全补1,例如16:10000,退位后01111。13的二进制:1101,结构上看只要第二个bit补1(1101->1111),再进位(1111->10000)就可以了。1101->1111->
我们来分析实现
首先把方法复制出来,加上一些日志方便分析:
static final int MAXIMUM_CAPACITY = 1 << 30; static final int tableSizeFor(int cap) {
System.out.println(Integer.toBinaryString(cap));
int n = cap - 1;
System.out.println(Integer.toBinaryString(n));
n |= n >>> 1;
System.out.println(Integer.toBinaryString(n));
n |= n >>> 2;
System.out.println(Integer.toBinaryString(n));
n |= n >>> 4;
System.out.println(Integer.toBinaryString(n));
n |= n >>> 8;
System.out.println(Integer.toBinaryString(n));
n |= n >>> 16;
System.out.println(Integer.toBinaryString(n));
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
} public static void main(String[] args) {
tableSizeFor(MAXIMUM_CAPACITY);
}
int n = cap - 1先退位,所以最终实现的结果是n的bit位全补1。
使用的最大容量+1,这样看日志比较清晰。
1000000000000000000000000000001
1000000000000000000000000000000
1100000000000000000000000000000
1111000000000000000000000000000
1111111100000000000000000000000
1111111111111111000000000000000
1111111111111111111111111111111
看到规律了吗?tableSizeFor首先获取最高位的1,二进制退位规则决定一定能够获取到最高位的1,然后进行不停的bit复制,1生2,2生4等等。int类型只有32位,所以复制到16位终止。
最后将n进位,即得到2的整次方,不过限定不能大于1>>30;
hashMap tableSizeFor 实现原理的更多相关文章
- Java中HashMap底层实现原理(JDK1.8)源码分析
这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap和JDK中的HashMap的也不是一样,原来他们没有指定JDK版本,很多文章都是旧版本JD ...
- HashMap底层实现原理(JDK1.8)源码分析
ref:https://blog.csdn.net/tuke_tuke/article/details/51588156 http://www.cnblogs.com/xiaolovewei/p/79 ...
- 一文搞定HashMap的实现原理和面试
原文 https://juejin.im/post/5d09f2d56fb9a07ec7551fb0 HashMap在日常开发中基本是天天见的,而且都知道什么时候需要用HashMap,根据Key存取 ...
- HashMap的工作原理
HashMap的工作原理 HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
- HashMap的工作原理深入再深入
前言 首先再次强调hashcode (==)和equals的真正含义(我记得以前有人会说,equals是判断对象内容,hashcode是判断是否相等之类): equals:是否同一个对象实例.注意,是 ...
- [转] HashMap的工作原理
HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此 ...
- 【转】HashMap的工作原理
很好的文章,推荐Java的一个好网站:ImportNew HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hasht ...
- 转:HashMap的工作原理,及笔记
HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此 ...
随机推荐
- sql创建备份表和复制数据到备份表
1.复制表结构及数据到新表CREATE TABLE 新表 SELECT * FROM 旧表 这种方法会将oldtable中所有的内容都拷贝过来,当然我们可以用delete from newtable; ...
- SpringMVC环境搭建和详解
1.Spring容器和SpringMVC容器是父子容器 1.1 SpringMVC容器可以调用Spring容器中的所有内容 1.2 图示 2.SpringMVC环境搭建 1.导入jar包 2.在web ...
- python对数据类型的相关操作
一.int的相关操作 int只有一个相关操作,bit_length() 用于计算一个数字的二进制长度 二.bool的相关操作 1.把数字转换成bool,除了0,返回的都是True a = 10 p ...
- Python开课复习-10/16
import random # random 随机数模块 # print(random.random()) #----float 大于0且小于1之间的小数# print(random.choice([ ...
- Oracle启动两个监听
接上篇:Oracle服务器修改IP后 Oracle服务器更换IP后,办公网络里面可以正常访问了,外地的同事,连了vpn 也可以访问,以为可以收工回家,突然又有同事过来说,机房的服务器ping不通新的i ...
- 2019.01.13 bzoj4137: [FJOI2015]火星商店问题(线段树分治+可持久化01trie)
传送门 题意:序列上有nnn个商店,有两种事件会发生: sss商店上进购标价为vvv的一个物品 求编号为[l,r][l,r][l,r]之间的位置买ddd天内新进购的所有物品与一个数xxx异或值的最大值 ...
- 关于Excel分析图插入到论文的问题
为了保证插入到latex图片不失真,可将Excel中的图进行如下操作: 1.将Excel分析图另存为.pdf格式: 2.利用Adobe acrobat裁剪掉空白的部分,另存为.eps格式: 3.将ep ...
- shell常见命令
awk awk是个很好用的东西,大量使用在linux系统分析的结果展示处理上.并且可以使用管道, input | awk '' | output 1.首先要知道形式 awk 'command' fi ...
- vue中的路由独享守卫的理解
1.vue中路由独享守卫意思就是对这个路由有一个单独的守卫,因为他的守卫方式于其他的凡是不太同 独享守卫于前置守卫使用方法大致是一样的 在路由配置的时候进行配置, { path:'/login', c ...
- shell 脚本学习
Shell简介 概述 Shell是一种具备特殊功能的程序,它提供了用户与内核进行交互操作的一种接口.它接收用户输入的命令,并把它送入内核去执行.内核是Linux系统的心脏,从开机自检就驻留在计算机的内 ...