为什么HashMap初始大小为16,为什么加载因子大小为0.75,这两个值的选取有什么特点?

内容转自:https://blog.csdn.net/Dazhu233/article/details/79596584

先看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,你放两个键值对就要扩容,太浪费空间了。

原文地址:https://www.cnblogs.com/lishiqi-blog/p/11329589.html

时间: 2024-08-07 02:32:53

为什么HashMap初始大小为16,为什么加载因子大小为0.75,这两个值的选取有什么特点?的相关文章

List、Map、set的加载因子,默认初始容量和扩容增量

首先,这三个概念说下.初始大小,就是创建时可容纳的默认元素个数:加载因子,表示某个阀值,用0~1之间的小数来表示,当已有元素占比达到这个阀值后,底层将进行扩容操作:扩容方式,即指定每次扩容后的大小的规则,比如翻倍等. 当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因子的系数小于等于1,意指  即当 元素个数 超过 容量长度*加载因子的系数 时,进

关于new HashMap&lt;&gt;(1)中1的理解(hashMap的加载因子)

新入公司,阅读代码的时候发现了一行代码,为 Map<String, String> map=new HashMap<>(1); 对于这个括号里面的1不能理解,于是查了资料,大概了解了. 解释如下:这个1被称为加载因子. API: /** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75).

ArrayList、Vector、HashMap、HashTable、HashSet的默认初始容量、加载因子、扩容增量

这里要讨论这些常用的默认初始容量和扩容的原因是: 当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因子的系数小于等于1,意指  即当 元素个数 超过 容量长度*加载因子的系数 时,进行扩容. 另外,扩容也是有默认的倍数的,不同的容器扩容情况不同. List 元素是有序的.可重复 ArrayList.Vector默认初始容量为10 Vector:线程

ArrayList、Vector、HashMap、HashSet的默认初始容量、加载因子、扩容增量

这里要讨论这些常用的默认初始容量和扩容的原因是: 当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因子的系数小于等于1,意指 即当 元素个数 超过 容量长度*加载因子的系数 时,进行扩容. 另外,扩容也是有默认的倍数的,不同的容器扩容情况不同. List 元素是有序的.可重复 ArrayList.Vector默认初始容量为10 Vector:线程安

HashMap 扩容 加载因子

HashMap: public HashMap(int initialCapacity, float loadFactor) { //初始容量不能<0 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); //初始容量不能 > 最大容量值,HashMap的最大容量值为2^30 if (initialCapa

android客户端加载网络大图片如何避免内存溢出

在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是基于BitmapFactory.Options类提供的方法定义指定的解码方式,设置inJustDecodeBounds属性为true,避免分配内存,返回一个null的Bitmap对象(包含outWidth,outHeightandoutMimeType),然后读取图片的尺寸和类型.再根据屏幕的高和宽对图片进行缩放,最后将缩放的图片加载到内存,主要代码如下: 1 Options opts = new Opt

libgdx学习记录16——资源加载器AssetManager

AssetManager用于对游戏中的资源进行加载.当游戏中资源(图片.背景音乐等)较大时,加载时会需要较长时间,可能会阻塞渲染线程,使用AssetManager可以解决此类问题. 主要优点: 1. 大多数资源加载器AssetLoader都是异步加载,可以避免阻塞渲染线程. 2. 通过引用计数来进行释放资源. 3. 通过一个对象来管理所有其他资源. 主要函数: load(path,type)加载某个路径的资源文件,后面type指定所要加载的资源类型.这个函数只是将资源文件加入到资源队列中,并不会

理解 HashMap 加载因子 loadFactor

加载因子是表示Hsah表中元素的填满的程度.若:加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.反之,加载因子越小,填满的元素越少,好处是:冲突的机会减小了,但:空间浪费多了. 冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小. 因此,必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的"时-空"矛盾的平衡与折衷.

自制Javascript分页插件,支持AJAX加载和URL带参跳转两种初始化方式,可用于同一页面的多个分页和不同页面的调用

闲话部分 最近闲着实在无聊,就做了点小东西练练手,由于原来一直在用AspNetPager进行分页,而且也进行了深度的定制与原有系统整合的也不错,不过毕竟是用别人的,想着看自己能试着做出来不能,后台的分页插件已经有比较成熟的了,那就自己试着写一个前台分页吧. 话不多说,先上效果图: 优点与缺点 来说说优缺点吧,首先AspNetPager是后台分页控件,所以在向客户端回传HTML文档之前生成HTML阶段 就会把分页代码生成完毕,然后回传,而JS是前端代码,就是HTML文档在服务器组织完毕往客户端传送