为什么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 & ...
随机推荐
- c/c++叉树的创建与遍历(非递归遍历左右中,不破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,不破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
- Windows Server 2016-域站点链接及复制时间调整
本章简单为大家介绍如何新建域链接并调整复制计划.生产环境中很多情况下需要我们手工去创建站点复制链接,并根据实际带宽情况调整复制计划以减轻网络压力等.站点内部署多台域控制器,域控制器间的 复制链接建议通 ...
- LeetCode算法题-Number of Boomerangs(Java实现)
这是悦乐书的第231次更新,第244篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第98题(顺位题号是447).给定平面中的n个点都是成对不同的,"回旋镖&qu ...
- WIndows 使用VS编译 Lua5
从Lua5.1开始官方给出的文件只有源代码和makefile文件了,官网给出的bulid方式也是在linux平台,如果只是想找个库使用下可以到这里来下载:http://joedf.ahkscript. ...
- #000 Python 入门第一题通过扩展,学到了更多的知识
#1写在前面的话 我觉得这样学习或许能够在学习的过程中事半功倍 第一道简单的python编写代码输出10行带标号的“Hello,world.”,具体效果参阅输入输出示例 1:Hello,world. ...
- Vue+Webpack常见问题(持续更新)
常识 1.computed计算属性,使用的属性必需在data里面声明. computed: { canLogin: function(){ //注意这里的依赖的属性必需在data里面声明 return ...
- ant.design React使用Echarts,实力踩坑
最近项目用到Echarts(以下用ec代替),于是照猫画虎得引入到团队的antd项目中,但是遇到2个棘手问题: 1. ec对dom不渲染,检查后发现,原来是全局存在id重复,所以使用React时,最好 ...
- (转)Spring Boot(五):Spring Boot Jpa 的使用
http://www.ityouknow.com/springboot/2016/08/20/spring-boot-jpa.html 在上篇文章Spring Boot(二):Web 综合开发中简单介 ...
- js判断是否是移动端自动跳转到wap页面代码
<script type="text/javascript"> function is_mobile(){ var regex_match=/(nokia|iphone ...
- Scrapy 框架 手动发送请求 POST 请求的发送
手动发送请求 import scrapy from choutiSpider.items import ChoutispiderItem class ChoutiSpider(scrapy.Spide ...