先看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,这两个值的选取有什么特点?的更多相关文章

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

    当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因 ...

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

    首先,这三个概念说下.初始大小,就是创建时可容纳的默认元素个数:加载因子,表示某个阀值,用0~1之间的小数来表示,当已有元素占比达到这个阀值后,底层将进行扩容操作:扩容方式,即指定每次扩容后的大小的规 ...

  3. Java集合类初始容量、加载因子、扩容增量

    当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因 ...

  4. 关于new HashMap<>(1)中1的理解(hashMap的加载因子)

    新入公司,阅读代码的时候发现了一行代码,为 Map<String, String> map=new HashMap<>(1); 对于这个括号里面的1不能理解,于是查了资料,大概 ...

  5. List加载因子和扩容因子

    List.Map.set的加载因子,默认初始容量和扩容增量 首先,这三个概念说下.初始大小,就是创建时可容纳的默认元素个数:加载因子,表示某个阀值,用0~1之间的小数来表示,当已有元素占比达到这个阀值 ...

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

    这里要讨论这些常用的默认初始容量和扩容的原因是: 当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全 ...

  7. [转]为什么Java中的HashMap默认加载因子是0.75

    前几天在一个群里看到有人讨论hashmap中的加载因子为什么是默认0.75. HashMap源码中的加载因子 static final float DEFAULT_LOAD_FACTOR = 0.75 ...

  8. HashMap默认加载因子为什么选择0.75?(阿里)

    Hashtable 初始容量是11 ,扩容 方式为2N+1; HashMap 初始容量是16,扩容方式为2N; 阿里的人突然问我为啥扩容因子是0.75,回来总结了一下: 提高空间利用率和 减少查询成本 ...

  9. HashMap 扩容 加载因子

    HashMap: public HashMap(int initialCapacity, float loadFactor) { //初始容量不能<0 if (initialCapacity & ...

随机推荐

  1. Python平时代码的一些知识

      os.walk的作用: def file_name(file_dir): for root, dirs, files in os.walk(file_dir): print(root) #当前目录 ...

  2. Oauth2.0[笔记]

    背景 如果资源服务器只是提供资源给自己的应用,使用帐号密码做身份认证倒没什么问题,但如果需要提供资源给第三方应用,就会出现第三方应用需要与资源服务器共享身份凭证,这时会出现几个问题: 1.第三方应用需 ...

  3. [Java] SpringMVC工作原理之二:HandlerMapping和HandlerAdapter

    一.HandlerMapping 作用是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecu ...

  4. 因PHP漏洞,超过4.5万个中国网站被攻击

    因PHP漏洞,超过4.5万个中国网站被攻击 日前据外媒ZDNet了解,超过4.5万个中国网站受到了试图进入网络服务器的不法分子攻击. 此次黑客利用中国网络安全公司VulnSpy在ExploitDB上发 ...

  5. IOT,笔记:avrdude: ser_open(): can't open device "\\.\COM3": 系统找不到指定的文件。

    1.下载驱动:https://www.arduino.cc/ 下载后解压 2.UNO板子以及驱动的相关设置 将UNO板子用数据线连接到电脑上,设置驱动: 打开设备管理器----->找到端口--- ...

  6. Spring Boot web API接口设计之token、timestamp、sign

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/vbirdbest/article/details/80789817一:token 简介Token:访 ...

  7. Spring Boot+Spring Security+JWT 实现 RESTful Api 权限控制

    摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能 ...

  8. 第一章 mysql的体系结构与存储引擎

    数据库从逻辑上可以分为两部分,一部分负责存储即文件系统,这部分有个更时髦的名字叫存储引擎,存储引擎负责如何把数据以及索引相关的内容以合适的形式组织并存储到磁盘上.另一部分为server部分,负责和用户 ...

  9. Spring Security(十七):5.8 Method Security

    From version 2.0 onwards Spring Security has improved support substantially for adding security to y ...

  10. BZOJ 5467 Slay the Spire

    BZOJ 5467 Slay the Spire 我的概率基础也太差了.jpg 大概就是这样,因为强化牌至少翻倍,所以打出的牌必定是全部的强化牌或者$k-1$个强化牌,然后剩余的机会打出最大的几个攻击 ...