摘要:结合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. 【ABAQUS 二次开发笔记】输出单元刚度矩阵

    目录 相关的关键字 必须的参数 可选参数 使用关键字 输出到mtx文件 输出到dat文件 参考资料 相关的关键字 *ELEMENT MATRIX OUTPUT 此keyword用于将元素刚度矩阵和质量 ...

  2. 国内四大骨干网与十大ISP服务商

    1.骨干网 几台计算机连接起来,互相可以看到其他人的文件,这叫局域网,整个城市的计算机都连接起来,就是城域网,把城市之间连接起来的网就叫骨干网.这些骨干网是国家批准的可以直接和国外连接的互联网.其他有 ...

  3. java学习-6-核心类:字符串StringJoiner 和数组一起玩

    public class Main { public static void main(String[] args) { String[] names = {"Bob", &quo ...

  4. 使用MCP C# SDK开发MCP Server + Client

    大家好,我是Edison. 近日被MCP刷屏了,刚好看到张队发了一篇文章提到MCP的官方C# SDK发布了预览版,于是手痒痒尝了一下鲜,写了一个DEMO分享给大家. MCP是什么鬼? MCP,全称是& ...

  5. Java 21 新特性

    Java 21 是 Java 语言的一次重要更新,引入了若干新的特性,提升了开发者的编程效率和代码质量.本文将详细介绍 Java 21 的新特性,包括基础概念.使用方法.常见实践以及最佳实践. 简介 ...

  6. MySQL 中使用索引一定有效吗?如何排查索引效果?

    MySQL 中使用索引一定有效吗?如何排查索引效果? 虽然索引是提升 MySQL 查询性能的常见手段,但并不是所有情况下索引都会有效.索引的使用取决于查询条件.数据分布.索引设计等多个因素.如果索引未 ...

  7. fiddler抓包常用辅助工具

    一.过滤器 1.hosts: 只展示内网或外网的hosts,internet(外网),Intranet(内网) 展示下面的hosts/隐藏下面的hosts/:选择后填写需要设置的hosts(地址前面的 ...

  8. markdown文本编辑器--核心功能(解析和渲染)

    开源项目地址 GitHub 开源地址(YtyMark-java) 欢迎提交 PR.Issue.Star ️! 1. 简述 YtyMark-java项目分为两大模块: UI界面(ytyedit-mark ...

  9. Asp.net core 少走弯路系列教程(六)C# 语法学习

    前言 新人学习成本很高,网络上太多的名词和框架,全部学习会浪费大量的时间和精力. 新手缺乏学习内容的辨别能力,本系列文章为新手过滤掉不适合的学习内容(比如多线程等等),让新手少走弯路直通罗马. 作者认 ...

  10. .net6 Api添加跨域

    参照:(7条消息) .net6使用最小api(8)- 开启跨域模式,通过扩展服务实现_hailang2ll的博客-CSDN博客 步骤: 一.在appsetting.json里添加配置文件 //配置文件 ...