为什么HashMap初始大小为16,为什么加载因子大小为0.75,这两个值的选取有什么特点?
先看HashMap的定义:
public class HashMap<K,V>extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
HashMap是AbstractMap的子类,实现了Map接口。
HashMap()
Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75). |
HashMap(int initialCapacity)
Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75). |
HashMap(int initialCapacity, float loadFactor)
Constructs an empty HashMap with the specified initial capacity and load factor. |
HashMap(Map<? extends K,? extends V> m)
Constructs a new HashMap with the same mappings as the specified Map. |
总共给出了4中构造方法。
1.HashMap() 不带参数,默认初始化大小为16,加载因子为0.75;
2.HashMap(int initialCapacity) 指定初始化大小;
3.HashMap(int initialCapacity ,float loadFactor)指定初始化大小和加载因子大小;
4.HashMap(Map<? extends K,? extends V> m) 用现有的一个map来构造HashMap。
先分析一下初始化代码
public HashMap(int initialCapacity,float loadFactor){
if(initialCapacity<0) //初始化大小小于0,抛出异常
throw new IllegalArgumentException("Illegal initial capacity: "
+initialCapacity);
if(initialCapacity>MAXIMUM_CAPACITY)//初始大小最大为默认最大值
initialCapacity=MAXIMUM_CAPACITY;
if(loadFactor <=0|| Float.isNaN(loadFactor)) //加载因子要在0到1之间
throw new IllegalArgumentException("Illegal load factor: "
+loadFactor);
this.loadFactor =loadFactor;
this.threshold=tableSizeFor(initialCapacity);
//threshold是根据当前的初始化大小和加载因子算出来的边界大小,
//当桶中的键值对超过这个大小就进行扩容
}
threshold=initialCapacity*loadFactor,桶中的键值对超过这个界限就把桶的容量变大。
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
这是第一次向桶中放入元素时(put方法)的一次扩容,因为初始化时并没有指定桶的大小
每次调用put方法后,都会进行验证,判断是否需要扩容
if (++size > threshold)
resize();
在看一下扩容的代码
Node<K,V>[] oldTab=table;
int oldCap=(oldTab==null)?0:oldTab.length;// 第一次扩容时旧的桶大小为0
int oldThr=threshold; //桶中键值对数量的边界大小。
int newCap,newThr=0; //新的桶大小和边界大小。
if(oldCap>0){ //这是后来扩容时进入的,第一次指定桶大小时不会进入这
if(oldCap>= MAXIMUM_CAPACITY){//最大只能这么大了
threshold=Integer.MAX_VALUE;
return oldTab;
}
else if((newCap=oldCap<<1)<MAXIMUM_CAPACITY&&
oldCap>=DEFALT_INITAL_CAPACITY)
newThr=oldThr<<1; //如果旧的桶大小扩大一倍还没有超过最大值,
//就把旧的桶大小和新的边界都乘2
}
else if(oldThr>0)
newCap=oldThr; //第一次扩容时会进入这里初始化桶的大小
if(newThr==0){ //根据初始化桶数组大小和加载因子已经是否越界来设置新边界
float ft=(float)newCap*loadFactor;
newThr=(newCap<MAXIMUM_CAPACITY && ft<(float)MAXIMUM_CAPACITY?
(int)ft:Integer.MAX_VALUE);
}
下面我们来讨论一个问题。
为什么默认初始化桶数组大小为16,为什么加载因子的大小为0.75,这两个值的选取有什么特点。
通过看上面的代码我们可以知道这两个值主要影响的threshold的大小,这个值的数值是当前桶数组需不需要扩容的边界大小,
我们都知道桶数组如果扩容,会申请内存空间,然后把原桶中的元素复制进新的桶数组中,这是一个比较耗时的过程。既然这样,那为何不把这两个值都设置大一些呢,threshold是两个数的乘积,设置大一些就不那么容易会进行扩容了啊。
原因是这样的,如果桶初始化桶数组设置太大,就会浪费内存空间,16是一个折中的大小,既不会像1,2,3那样放几个元素就扩容,也不会像几千几万那样可以只会利用一点点空间从而造成大量的浪费。
加载因子设置为0.75而不是1,是因为设置过大,桶中键值对碰撞的几率就会越大,同一个桶位置可能会存放好几个value值,这样就会增加搜索的时间,性能下降,设置过小也不合适,如果是0.1,那么10个桶,threshold为1,你放两个键值对就要扩容,太浪费空间了。
为什么HashMap初始大小为16,为什么加载因子大小为0.75,这两个值的选取有什么特点?的更多相关文章
- ArrayList、Vector、HashMap、HashSet的默认初始容量、加载因子、扩容增量
当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因 ...
- List、Map、set的加载因子,默认初始容量和扩容增量
首先,这三个概念说下.初始大小,就是创建时可容纳的默认元素个数:加载因子,表示某个阀值,用0~1之间的小数来表示,当已有元素占比达到这个阀值后,底层将进行扩容操作:扩容方式,即指定每次扩容后的大小的规 ...
- Java集合类初始容量、加载因子、扩容增量
当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因 ...
- 关于new HashMap<>(1)中1的理解(hashMap的加载因子)
新入公司,阅读代码的时候发现了一行代码,为 Map<String, String> map=new HashMap<>(1); 对于这个括号里面的1不能理解,于是查了资料,大概 ...
- List加载因子和扩容因子
List.Map.set的加载因子,默认初始容量和扩容增量 首先,这三个概念说下.初始大小,就是创建时可容纳的默认元素个数:加载因子,表示某个阀值,用0~1之间的小数来表示,当已有元素占比达到这个阀值 ...
- ArrayList、Vector、HashMap、HashTable、HashSet的默认初始容量、加载因子、扩容增量
这里要讨论这些常用的默认初始容量和扩容的原因是: 当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全 ...
- [转]为什么Java中的HashMap默认加载因子是0.75
前几天在一个群里看到有人讨论hashmap中的加载因子为什么是默认0.75. HashMap源码中的加载因子 static final float DEFAULT_LOAD_FACTOR = 0.75 ...
- HashMap默认加载因子为什么选择0.75?(阿里)
Hashtable 初始容量是11 ,扩容 方式为2N+1; HashMap 初始容量是16,扩容方式为2N; 阿里的人突然问我为啥扩容因子是0.75,回来总结了一下: 提高空间利用率和 减少查询成本 ...
- HashMap 扩容 加载因子
HashMap: public HashMap(int initialCapacity, float loadFactor) { //初始容量不能<0 if (initialCapacity & ...
随机推荐
- Hive中笔记 :三种去重方法,distinct,group by与ROW_Number()窗口函数
一.distinct,group by与ROW_Number()窗口函数使用方法 1. Distinct用法:对select 后面所有字段去重,并不能只对一列去重. (1)当distinct应用到多个 ...
- CentOS 7.0安装
CentOS 7.0安装 本次通过虚拟机的方法安装CentOS 7.0操作系统,开启虚拟机后会出现以下界面 1.选择第一项,Install CentOS 7 (安装CentOS 7),进入下面的界面 ...
- 【PAT】B1011 A+B 和 C
注意数据的范围,使用long long就行了 #include<stdio.h> int main(){ int N;scanf("%d",&N); for(i ...
- Win 10 和 Linux 双系统,从硬盘删除Linux分区,Win 10引导修复
由于安装双系统后,Linux 用的比较少.因此,从Win 10 磁盘管理中删除了linux 占用的磁盘空间,重启后无法进入win 10 ,出现如下情况: 有人提出,此时需要重装系统,并不用如此麻烦,通 ...
- LeetCode算法题-Find All Numbers Disappeared in an Array(Java实现)
这是悦乐书的第232次更新,第245篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第99题(顺位题号是448).给定一个整数数组,其中1≤a[i]≤n(n =数组的大小) ...
- CSS 浮动(float)与定位(position)
一.浮动 1.三个属性:left.right.none. 2.特点:容易造成父项塌陷,故在父项需要清除浮动 3.父项塌陷现象 4.父项塌陷解决方案(建议使用):清除浮动 .parent:after{ ...
- spring jwt springboot RESTful API认证方式
RESTful API认证方式 一般来讲,对于RESTful API都会有认证(Authentication)和授权(Authorization)过程,保证API的安全性. Authenticatio ...
- [HEOI2016/TJOI2016]排序
嘟嘟嘟 首先这题的暴力是十分好写的,而且据说能得不少分. 正解写起来不难,就是不太好想. 根据做题经验,我想到了给这个序列转化成01序列,但是接下来我就不会了.还是看了题解. 因为查询只有一个数,所以 ...
- P1678 烦恼的高考志愿(二分)
emmmm,我感觉我在解题的过程中还是有点吃亏的,因为,我知道是二分,只是大概知道怎么分,没有管这道到底是需要怎样的二分.然后在题上卡了很久. 思路:要找到填报学校的录取线x和自己的分数y的绝对值最小 ...
- 微信小程序PHP 微信支付接口调用
小程序端 /** * 微信支付接口 */ wxPaymoney:function (out_trade_no, true_money){ //out_trade_no 后台统一下单接口需要用 var ...