Java中HashMap 初始化时容量(参数)如何设置合适?
问题引入
注:本文代码源自java 9。
- 阿里的插件对于初始化HashMap时,调用无参构造方法,提示如下:

- 那么问题来了,如果已知需要向 map 中 put n次,那么需要设定初始容量为多少?
- 单纯的我今天上午还认为是合理的容量是 n + 1 即可,直到看了源码;
- 应注意,
map.size获取的是当前map中键值对的个数,而不是容量。
当初始化的时候,没有指定容量,情况如何?
- 直接调用如下构造函数(无参)
/**
* Constructs an empty {@code HashMap} with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
- 首先应该清楚,loadFactor 是 装载因子(通俗来讲,若值为0.5f,那么当map元素达到 容量*0.5f的时候,就应该做扩容等操作)
- HashMap对象实际上只有 threshold(临界值,阈值) 属性,每次put()后,需要比较的是阈值与size(map中已有键值对的个数)
- 至于容量属性,实际上各个方法中用的是
transient Node<K,V>[] table;中 table 的长度 - Hashmap结构可参考 http://www.importnew.com/20386.html
- 本构造方法上说明:构造了一个空的HashMap,默认容量为16,装载因子为0.75。
- 然而可以清楚地看到,此构造方法只是将常量
DEFAULT_LOAD_FACTOR赋值给装载因子,但没有设定容量。 - 当调用
put()时,如下方式设定了初始容量。 - 调用
putVal(hash(key), key, value, false, true)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
- 在putVal()中,进入第一个if,调用resize()
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//省略无关代码
}
- 在下方代码中,oldCap 与 oldThr 初始都为0,故直接进入else{}
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0)
//省略无关代码
else if (oldThr > 0) // initial capacity was placed in threshold
//省略无关代码
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;//设定 newCap = 16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//设定 newThr = 16 * 0.75 = 12
}
threshold = newThr;//将对象的 threshold(临界值,阈值) 属性设置为 newThr
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = //将对象的 table 属性设置为刚实例化的 newTab
//省略无关代码
return newTab;//并返回
- 综上,默认无参的构造方法设定装载因子为0.75f,初始容量为16在调用put()时,设置为16。
当我们指定容量为n的时候,情况如何?
即:
Map<String,String> map = new HashMap<>(n);当指定容量时,调用如下构造方法:
/**
* Constructs an empty {@code HashMap} with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
- 此方法将自定义容量作为参数1,将默认常量0.75f作为参数2,调用如下构造方法。
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);
}
- 此方法先做了对于容量与装载因子值的合理性做了判断与处理;
- 设定装载因子值为0.75f,设置threshold 属性为 tableSizeFor()参数为n时的返回值。
- 对于
tableSizeFor(initialCapacity),如下:
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;
}
- 也就是传入 n 的时候,返回了 第一个 比 n 大的 2的幂(2.4.8.16.32.64...)的数,例 n为 10,返回16;n为17 返回 32.
- 具体实现原理可参考:http://blog.csdn.net/fan2012huan/article/details/51097331
- 同上,对于在put方法中,对象的table属性仍然为null,还是需要resize();
- 本次调用与上次不同在于,threshold属性 已经被赋值,假设值为
m(m>0);
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;//oldCap 值 为 0
int oldThr = threshold;//oldThr 值 为 m,m > 0
int newCap, newThr = 0;
if (oldCap > 0) {
//省略无关代码
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;//newCap 被赋值为 m
//省略无关代码
if (newThr == 0) {
float ft = (float)newCap * loadFactor;//ft为m*0.75
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
//在newCap 与ft 小于最大容量时(不满足时不讨论),执行 newThr = ft
}
threshold = newThr;//threshold 被再次赋值为 ft--->m*0.75
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;//容量赋值为 m
//省略无关代码
return newTab;
}
- 综上:当我们构造新的HashMap的时,传入容量为n(假设为9),构造方法根据n得到阈值m(16)
当调用put()时,直接将阈值设置为m*0.75(12) ,将容量设置为 m(16) - 也就是说,当n不是2的幂的时候,会根据n找到合适的数 m,并认为m是新的容量,阈值是m*0.75!
说了那么多,初始化的时候,到底怎么设置参数n
- 如下是put方法的最后一步,需要进行的如下方式的验证:
if (++size > threshold)
resize();
- 为了防止调用resize(),需要保证元素数量小于阈值(不可等于!)。
- 综上,需要根据调用put(key,value)的次数
count(准确的说是key不重复时),找到 第一个 比 n 大的 2的幂的数m
判断装载因子loadFactor(默认0.75)*m是否仍然不小于count,若满足则设置为count,否则设置为2*m或者m+1 - 例1,当我们需要在map里面装 100个元素时,已知128是第一个大于100并且是2的幂的数,但128*0.75 = 96 还是小于100,
那么显然129-256(闭区间)作为初始化的参数更合适。 - 例2,当我们需要在map里面装 80个元素时,已知128是第一个大于80并且是2的幂的数,且128*0.75 = 96 还是大于80,
那么显然64-128(左开右闭)作为初始化的参数更合适。 - 例3,当我们需要在map里面装 12个元素时,应选用16为初始容量!
参考文章:http://blog.csdn.net/fan2012huan/article/details/51087722
http://yikun.github.io/2015/04/01/Java-HashMap工作原理及实现/
https://www.cnblogs.com/coderxuyang/p/3718856.html
Java中HashMap 初始化时容量(参数)如何设置合适?的更多相关文章
- Java 中 HashMap 初始化时赋值
1.HashMap 初始化的文艺写法 HashMap 是一种常用的数据结构,一般用来做数据字典或者 Hash 查找的容器.普通青年一般会这么初始化:HashMap<String, Strin ...
- Java中HashMap的初始容量设置
根据阿里巴巴Java开发手册上建议HashMap初始化时设置已知的大小,如果不超过16个,那么设置成默认大小16: 集合初始化时, 指定集合初始值大小. 说明: HashMap使用HashMap(in ...
- java在hashmap初始化时赋初值
Java中的HashMap是一种常用的数据结构,一般用来做数据字典或者Hash查找的容器. 一般我们初始化并赋初值是这样做的: HashMap<String, Object> map = ...
- 【转】 java中HashMap详解
原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...
- java中HashMap详解(转)
java中HashMap详解 博客分类: JavaSE Java算法JDK编程生活 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成 ...
- java集合(2)- java中HashMap详解
java中HashMap详解 基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 H ...
- Java中HashMap的实现原理
最近面试中被问及Java中HashMap的原理,瞬间无言以对,因此痛定思痛觉得研究一番. 一.Java中的hashCode和equals 1.关于hashCode hashCode的存在主要是用于查找 ...
- JAVA中hashmap的分析
从http://blog.csdn.net/luanlouis/article/details/41576373?utm_source=tuicool&utm_medium=referral学 ...
- java中HashMap的设计精妙在哪?
摘要:本文结合图解和问题,教你一次性搞定HashMap 本文分享自华为云社区<java中HashMap的设计精妙在哪?用图解和几个问题教你一次性搞定HashMap>,作者:breakDaw ...
随机推荐
- 310实验室OTL问题(2)
1.PyOptimization代码学习心得 (1).该部分由三个分块组成(按完成的相应的任务).第一部分,运行函数. 以optimization.py为入口程序,其思路是:首先获取路径,然后,初始化 ...
- 记录web项目部署到阿里云服务器步骤
(使用 web项目.阿里云服务器.Xftp.Xshell),敬请参考和指正 1.将要部署的项目打包成WAR文件格式,可以在MyEclipse.Eclipse都可以完成打包,如下图: 2.安装Xshel ...
- 使用 Capistrano 进行自动化部署
最近在折腾这个,弄了好多次都不成功,看了官方文档和很多博客,都没有说清楚,因此,我觉得有必要把它记录下来,以帮助更多像我这样被弄得烦躁的人. 首先是安装,其实 Ubuntu 上面安装 Capistra ...
- 少走冤枉路!带你走过SNMP的那些坑
SNMP(Simple Network Management Protocol)即简单网络管理协议,是在网络与系统监控领域中,最常使用的一种数据采集技术.尽管这个协议非常简单,但在大规模IT环境监测中 ...
- ansible相关
上图为ansible的基本架构,从上图可以了解到其由以下部分组成: 核心:ansible 核心模块(Core Modules):这些都是ansible自带的模块 扩展模块(Custom Modules ...
- mysql 约束条件 外键 forigen key 介绍
外键 forigen key作用 :建立表之间的关系 什么是外键 员工信息表有这些字段:id号 姓名 性别 员工所在部门名 部门描述信息 公司有3个部门,但是有1个亿的员工,那意味着 员工所对应的部 ...
- [3D]第一人称相机类Camera
自己根据C++ D3D的源码改写一个相机类(第一人称). using System; using System.Collections.Generic; using System.Linq; usin ...
- 从WW中剥离一个三维场景框架
从WW中剥离一个三维场景框架,初步实现的一个.可以绘制一个三角形,但是不能够控制摄像机,没有增加鼠标事件.没有投影,世界变幻之类的东西.以后会不断学习逐步增加进来. 下载地址 下载V1.0.0.2
- Educational Codeforces Round 58 Solution
A. Minimum Integer 签到. #include <bits/stdc++.h> using namespace std; #define ll long long ll l ...
- sqlite的事务和锁,很透彻的讲解 【转】
原文:sqlite的事务和锁 http://3y.uu456.com/bp-877d38906bec097sf46se240-1.html 事务 事务定义了一组SQL命令的边界,这组命令或者作为一个整 ...