我们都知道,对于HashMap来说,数组的容量为2的倍数,但是我们可以在创建map的时候传入一个数组的大小

此时,这个初始化数组大小会传给一个双参的构造器

1. 创建HashMap

  1. public static void main(String[] args) {
  2. HashMap<Integer, String> map = new HashMap<>(10);
  3. }

2. 构造器

  1. public HashMap(int initialCapacity) {
  2. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  3. }
  4. public HashMap(int initialCapacity, float loadFactor) {
  5. if (initialCapacity < 0)
  6. throw new IllegalArgumentException("Illegal initial capacity: " +
  7. initialCapacity);
  8. if (initialCapacity > MAXIMUM_CAPACITY)
  9. initialCapacity = MAXIMUM_CAPACITY;
  10. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  11. throw new IllegalArgumentException("Illegal load factor: " +
  12. loadFactor);
  13. this.loadFactor = loadFactor;
  14. //阈值(HashMap中最多能容纳的元素个数)
  15. // 阈值 = 数组长度 * 负载因子
  16. // 但是注意,刚初始化的时候阈值用来临时保存用户指定的初始容量或者默认初始容量16
  17. // 只有在第一次inflateTable()扩容之后,才会用计算出的正确的容量乘以负载因子赋值
  18. //此时传入的initialCapacity是10,会调用tableSizeFor方法,将其转为大于等于它的一个2的幂次数,例如7转为8,8转为8,10就转为16
  19. this.threshold = tableSizeFor(initialCapacity);
  20. }

3. tableSizeFor具体实现

  1. static final int tableSizeFor(int cap) {
  2. //首先减一操作是为了如果本身就为2的幂次数(8,16)的话,就使其降为一个小于2的幂次的数
  3. //例如我们传入的10,对应的2进制为00000000 00000000 00000000 00001010
  4. int n = cap - 1;
  5. //减后
  6. //00000000 00000000 00000000 00001001
  7. n |= n >>> 1;
  8. //右移后
  9. //00000000 00000000 00000000 00000100
  10. //之后再与自身相或也就是上面两个二进制相或
  11. //00000000 00000000 00000000 00001101
  12. n |= n >>> 2;
  13. //再次右移两位
  14. //00000000 00000000 00000000 00000011
  15. //再或
  16. //00000000 00000000 00000000 00001111
  17. //此时我们可以看到我们想要的效果了
  18. //将低位全部置为1!
  19. //后面这些也是一样的操作,因为int是32位的
  20. n |= n >>> 4;
  21. n |= n >>> 8;
  22. n |= n >>> 16;
  23. //之后对 与完的数字进行+1操作,(这些判断我就不说了)
  24. //00000000 00000000 00000000 00010000
  25. //此时+1后变为了一个2的幂次
  26. return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
  27. }

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解析的更多相关文章

  1. JDK8 HashMap 源码解析

    HashMap中数据结构 在jdk1.7中,HashMap采用数组+链表(拉链法).因为数组是一组连续的内存空间,易查询,不易增删,而链表是不连续的内存空间,通过节点相互连接,易删除,不易查询.Has ...

  2. HashMap源码解析 非原创

    Stack过时的类,使用Deque重新实现. HashCode和equals的关系 HashCode为hash码,用于散列数组中的存储时HashMap进行散列映射. equals方法适用于比较两个对象 ...

  3. Java中的容器(集合)之HashMap源码解析

    1.HashMap源码解析(JDK8) 基础原理: 对比上一篇<Java中的容器(集合)之ArrayList源码解析>而言,本篇只解析HashMap常用的核心方法的源码. HashMap是 ...

  4. 最全的HashMap源码解析!

    HashMap源码解析 HashMap采用键值对形式的存储结构,每个key对应唯一的value,查询和修改的速度很快,能到到O(1)的平均复杂度.他是非线程安全的,且不能保证元素的存储顺序. 他的关系 ...

  5. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  6. 史上最全HashMap红黑树解析

    HashMap红黑树解析 红黑树介绍 TreeNode结构 树化的过程 红黑树的左旋和右旋 TreeNode的左旋和右旋 红黑树的插入 TreeNode的插入 红黑树的删除 TreeNode的删除节点 ...

  7. HashMap源码解析和设计解读

    HashMap源码解析 ​ 想要理解HashMap底层数据的存储形式,底层原理,最好的形式就是读它的源码,但是说实话,源码的注释说明全是英文,英文不是非常好的朋友读起来真的非常吃力,我基本上看了差不多 ...

  8. 详解HashMap源码解析(下)

    上文详解HashMap源码解析(上)介绍了HashMap整体介绍了一下数据结构,主要属性字段,获取数组的索引下标,以及几个构造方法.本文重点讲解元素的添加.查找.扩容等主要方法. 添加元素 put(K ...

  9. 【Java深入研究】9、HashMap源码解析(jdk 1.8)

    一.HashMap概述 HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现.与HashTable主要区别为不支持同步和允许null作为key和value.由于HashMap不是线程 ...

随机推荐

  1. 一 MongoDB入门

    一.MongoDB概念解析(对比MySQL学习): 举个例子: MongoDB可视化操作工具:推荐Robomongo 二.MongoDB默认的概念: 1.MongoDB的单个实例可以容纳多个独立的数据 ...

  2. OVN架构

    原文地址 OVN架构 1.简介 OVN,即Open Virtual Network,是一个支持虚拟网络抽象的系统. OVN补充了OVS的现有功能,增加了对虚拟网络抽象的原生(native)支持,比如虚 ...

  3. rabbitMq镜像集群

    rabbitMq延迟投递的方案 1 把消息记录到数据路,通过定时器进行刷新 2 TTL 加上死信队列 :通过路由把过期的消息同步到死信队列,通过死信队列的消费者进行消费 3

  4. nacos在nginx下集群以及数据库问题

    持久化mysql时指定数据库编辑application.properties spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql ...

  5. Ajax的GET,POST方法传输数据和接收返回数据

    //首先创建一个Ajax对象 function ajaxFunction(){ var xmlHttp; try{ // Firefox, Opera 8.0+, Safari xmlHttp=new ...

  6. ES6基础之let、const

    es6的块级作用域通俗的讲就是一对花括号中的区域(声明对象的花括号不是块级作用域),块级作用域可以嵌套. let: 1.le声明的变量只在当前(块级)作用域内有效. 2.let声明的变量不能被重复声明 ...

  7. PyTorch安装及试用 基于Anaconda3

    设置Torch国内镜像 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ ...

  8. python 文件批量改名重命名 rename

    path = '/Volumes/Seagate/dev/imgs/' os.chdir(path) print('cwd: ', os.getcwd()) for f in os.listdir(' ...

  9. GoLang设计模式3 - 抽象工厂模式

    之前我们介绍了工厂设计模式,现在我们再看一下抽象工厂设计模式.抽象工程模式顾名思义就是对工厂模式的一层抽象,也是创建型模式的一种,通常用来创建一组存在相关性的对象. UML类图大致如下: 类图比较复杂 ...

  10. Mybatis(二)——全局配置文件

    一.在正文上方直接添加目录. 1.二级标题***申请开通js权限 2.添加js脚本到页脚Html代码 数组:采用一段连续的存储单元来"存储"数据.对于"指定下标" ...