摘要:结合HashMap源码,介绍HashMap如何确定初始化容量,其最大容量是多少。

  更多关于HashMap的知识点,请戳《HashMap知识点梳理、常见面试题和源码分析》。

  本文基于Java 17进行分析。

  什么是HashMap的容量?容量就是HashMap中的数组大小或者桶的数量,是由 capacity 这个参数确定的。初始容量只是哈希表在创建时的容量。

  大家都知道HashMap是采用的懒加载机制,也就是说在执行new HashMap()的时候,构造方法并没有在构造出HashMap实例的同时也把HashMap实例里所需的数组给初始化。那么,什么时候才去初始化里面的数组呢?答案是在第一次用到数组的时候才会去初始化它,就是在向HashMap里面添加元素的时候。而初始化数组时,它的容量是怎么确定的呢?有两种情况:

第一种是调无参构造函数初始化实例。此时默认的数组初始化长度就是16,在后续添加元素时,进行数组初始化。

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

  第二种是调用带数组容量参数的构造函数。

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

或者


/**
* 如果构造函数传入的值大于该数,则替换成该数。
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30; // ①
/**
* The next size value at which to resize (capacity * load factor).
* 数组扩容的阈值
* @serial
*/
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold; 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);
}

  显而易见,上面那个构造方法执行的时候调用的就是下一个构造方法 this(initialCapacity, DEFAULT_LOAD_FACTOR)。当你调用带参构造器初始化一个指定数组容量的HashMap时,构造器会根据输入的参数提前计算出数组实际的长度,这个值也是在首次添加元素时起作用。计算的逻辑在函数 tableSizeFor(int cap) 中,源码如下:

static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; // ③ 判断是否超过最大容量
}

  它对入参cap减一之后使用了无符号右移,然后进行或运算,将n-1得到的值转成2进制之后,从1的最高位开始将低位全部转化为1,再加1之后就可以得到一个2^n的数。

HashMap的最大容量是2^30

  HashMap的最大容量是多少?容量默认是16,也可以构造时传入,最大值是1<<30,即2^30,这个在源码①的注释中已经明确说明。首先必须理解操作符 <<,它是左移操作符,表示对二进制进行左移。通常情况下,1 << x 等于 2^x

  上一节中②和③所标记的代码表明,如果要存的元素数目大于 MAXIMUM_CAPACITY,HashMap方法还把数组大小capacity强制设置成 MAXIMUM_CAPACITY

  综上所述,HashMap限制数组大小最大值有两个地方,其一就是初始化时调用tableSizeFor()函数,它会将容量置为 2的幂次,并保证不超过MAXIMUM_CAPACITY。其二就是调用扩容函数resize()进行容量翻倍时。如果容量达到MAXIMUM_CAPACITY时允许再扩容,新数组的容量就是 1 << 31,这会造成整型溢出,故Integer.MAX_VALUE是HashMap的最终容量。

HashMap的最大扩容阈值是2^31-1

在扩容函数resize()中有一个强制设置阈值大小的代码片段:

if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}

hreshold是HashMap所能容纳的最大数据量的Node(键值对)个数,threshold = length * Load factor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。

  在这里可以看到,其实 HashMap 扩容阈值threshold的最大值就是Integer.MAX_VALUE=2^31-1;

刷一道面试题

  今天看一个关于HashMap的性能问题:如果HashMap只装载100个元素,new HashMap(int x)中x的最佳值是多少,为什么?

  答案:256。

  解析:题意是令加载因子取默认值0.75,此时HashMap的初始容量可以设为100/0.75 = 133.33,向上取整为134。

无论你的HashMap(int x)中的x设置为多少,HashMap的大小都是2n,而且2n是大于x的第一个数,故大于134的第一个2^n无疑是256。

结束语

  以上就是这篇文章的全部内容了,希望本文对道友的学习或者工作能带来一定的帮助,如有疑问请留言交流。Wiener在此祝各位生活愉快!工作顺利!

  人情早晚有用完的时候,如若自己拥有足够的实力,定能赢得别人的尊重。更何况没有人欠我们人情呢!为人处世尚且如此,披星戴月的码农是不是要刻苦钻研,拓展技术栈的广度和深度呢?

Reference

HashMap如何计算初始化容量,最大容量是多少的更多相关文章

  1. hashmap 为什么初始化容量是2的幂次方

    个人理解 做下记录,不正确的地方望不吝赐教 这是hashmap初始化容量时候 对容量大小做的处理,保证初始化容量为最近的2的幂次方(JDK1.8) static final int tableSize ...

  2. hashMap为啥初始化容量为2的次幂

    原文 https://blog.csdn.net/sd_csdn_scy/article/details/57083619hashMap源码获取元素的位置: static int indexFor(i ...

  3. 阿里巴巴Java开发手册建议创建HashMap时设置初始化容量,但是多少合适呢?

    集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生. 关于HashMap,很多人都对他有一些基本的了解,比如他和hashtab ...

  4. 关于HashMap初始化容量问题

    使用阿里云代码规范插件扫描后出现以下提示: hashmap should set a size when initalizing,即hashmap应该在初始化时设置一个大小 在网上搜到一篇讲解(htt ...

  5. HashMap初始化容量过程

    集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生.在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map&l ...

  6. HashMap的初始化,到底都做了什么?

    HashMap的初始化,到底都做了什么? HashMap初始化参数都是什么?默认是多少? 为什么建议初始化设置容量? tableSizeFor方法是做什么的? 如何获取到一个key的hash值?及计算 ...

  7. 为啥HashMap的默认容量是16?

    集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生. 在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map& ...

  8. 为啥HashMap的默认容量是16

    集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生. 在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map& ...

  9. HashMap:为什么容量总是为2的n次幂

    HashMap:为什么容量总是为2的n次幂1).HashMap是根据key的hash值决定key放到哪个桶中,通过tab[i = (n - 1) & hash]公式计算得出 这里的n是Hash ...

  10. Java中HashMap的初始容量设置

    根据阿里巴巴Java开发手册上建议HashMap初始化时设置已知的大小,如果不超过16个,那么设置成默认大小16: 集合初始化时, 指定集合初始值大小. 说明: HashMap使用HashMap(in ...

随机推荐

  1. Python基础-模块和面向对象-shutil、re、bs4、requests模块

    概要: 模块 自定义模块(已经讲了) 内置模块 shutil re 正则表达式 第三方模块 requests 模块 bs4 模块 面向对象: 面向对象(Object-Oriented Programm ...

  2. Ubuntu修改密码和用户名

    Ubuntu是一个Linux操作系统,修改密码和用户名是有危险的动作,请谨慎修改. 一.Ubuntu修改密码和用户名 Ubuntu更改密码步骤:1.进入Ubuntu,打开一个终端,输入 sudo su ...

  3. Windows编程----进程:命令行参数

    什么是进程的命令行参数 每个进程在启动(双击exe启动.cmd命令行启动或者由其他程序通过CreateProcess启动)的时候,都会有一个命令行参数给它.命令行的参数以空格区分.这个命令行总是不为空 ...

  4. ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm).

    报错 ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared me ...

  5. 漏洞编号CVE-2022-27191 漏洞公告 ALINUX3-SA-2024:0050: container-tools:rhel8 安全和BUG修复更新

    基于Debian的系统(如Ubuntu),使用apt sudo apt-get update sudo apt-get install --only-upgrade container-selinux ...

  6. rot-偏移,ascii,md5爆破

    题目: 破解下面的密文: 83 89 78 84 45 86 96 45 115 121 110 116 136 132 132 132 108 128 117 118 134 110 123 111 ...

  7. 渗透技巧——CDN绕过

    渗透技巧--CDN绕过 一.前言: 在渗透站点的时候常常会遇见站点有CDN加速情况,就无法准确的找到目标IP.首先是检测如何发现有无CDN,然后才能说绕过的问题. 二.检测有无CDN: 首先有以下几种 ...

  8. 区块链特辑——solidity语言基础(二)

    Solidity语法基础学习 四.函数类型: 函数 Function function FnName [V] [SM] [return (--)] {} ·[V]:Visibility,可见性: ·[ ...

  9. 请确保在应用程序配置文件的“entityFramework”节中注册了该提供程序

    Exception information: Exception type: MetadataException Exception message: 指定的架构无效. 错误: Model.LW.OT ...

  10. exim4

    exim4 一台 debian 机器日常执行 apt update 后发现需要更新如下软件包, 之前没见过, 特此记录下. root@idebian:~# apt list --upgradable ...