为什么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 & ...
随机推荐
- sql生成连续日期(年份、月份、日期)
此随笔主在分享日常可能用到的sql函数,用于生成连续日期(年份.月份.日期) 具体的看代码及效果吧! -- ============================================= ...
- 在Django中接收文件并存储
首先是一个views函数的例子 def get_user_profiles(request): if request.method == 'POST': myFile = request.FILES. ...
- 删除排序链表中的重复元素的golang实现
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 输入: ->-> 输出: -> 输入: ->->->-> 输出: ->-> 我们先 ...
- 《生命》第二集:Reptiles and Amphibians (爬行和两栖动物)
第二集也是一个个动物的片段,不过集中在爬行和两栖类动物上. 印度尼西亚的瀑布蟾蜍进化出神器强有力的脚,能够抓牢很多物体,是逃生的手段,同一环境下,卵石蟾蜍,能够缩紧全身肌肉,眼山坡下滑,是另一种逃生是 ...
- python 管道、数据共享、进程池
一.管道(Pipe)(了解) (详情参考:https://www.cnblogs.com/clschao/articles/9629392.html) 进程间通信(IPC)方式二:管道(不推荐使用,了 ...
- [Java] SpringMVC工作原理之四:MultipartResolver
MultipartResolver 用于处理文件上传,当收到请求时 DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isM ...
- Hibernate知识点总结(一)
前言:学习的过程在于不断的总结与思考,这里记下笔者在学习过程中,所遇到的知识点,增加对框架掌握的熟悉程度. 1.环境的搭建 通过maven可以轻松搭建hibernate的环境. <propert ...
- 【CQOI2006】凸多边形
1713 -- [CQOI2006]凸多边形 Description 逆时针给出n个凸多边形的顶点坐标,求它们交的面积.例如n=2时,两个凸多边形如下图: 则相交部分的面积为5.233. Input ...
- 深度学习之Attention Model(注意力模型)
1.Attention Model 概述 深度学习里的Attention model其实模拟的是人脑的注意力模型,举个例子来说,当我们观赏一幅画时,虽然我们可以看到整幅画的全貌,但是在我们深入仔细地观 ...
- P1897 电梯里的爱情
简单模拟: 没什么好说的,因为范围比较水,所以直接按题意直接模拟1就好 #include<iostream> using namespace std; #define ll long lo ...