您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

HashMap的初始容量和加载因子

时间:2019-09-09 13:18:06  来源:  作者:

注:本文所有代码示例均基于 JDK8。

从源码出发

默认值

通过查看 HashMap 的源码可以得知其默认的初始容量为 16,默认的加载因子为 0.75。

/**
 * The default initial capacity - MUST be a power of two.
 * 默认的初始容量(必须是2的N次幂),默认为2^4=16
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
 * The load factor used when none specified in constructor.
 * 默认的加载因子为0.75
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

构造方法

/**
 * Constructs an empty <tt>HashMap</tt> with the specified initial
 * capacity and load factor.
 *
 * @param initialCapacity the initial capacity
 * @param loadFactor the load factor
 * @throws IllegalArgumentException if the initial capacity is negative
 * or the load factor is nonpositive
 */
public HashMap(int initialCapacity, float loadFactor) {
		if (initialCapacity < 0)
				throw new IllegalArgumentException("Illegal initial capacity: " +
																					 initialCapacity);
		if (initialCapacity > MAXIMUM_CAPACITY)
				initialCapacity = MAXIMUM_CAPACITY;
		if (loadFactor <= 0 || Float.isNaN(loadFactor))
				throw new IllegalArgumentException("Illegal load factor: " +
																					 loadFactor);
		this.loadFactor = loadFactor;
		this.threshold = tableSizeFor(initialCapacity);
}
/**
 * Constructs an empty <tt>HashMap</tt> with the specified initial
 * capacity and the default load factor (0.75).
 *
 * @param initialCapacity the initial capacity.
 * @throws IllegalArgumentException if the initial capacity is negative.
 */
public HashMap(int initialCapacity) {
		this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
 * Constructs an empty <tt>HashMap</tt> with the default initial capacity
 * (16) and the default load factor (0.75).
 */
public HashMap() {
		this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

一般情况下都是通过这三种构造方法来初始化 HashMap 的。通过默认的无参构造方法初始化后 HashMap 的容量就是默认的16,加载因子也是默认的0.75;通过带参数的构造方法可以初始化一个自定义容量和加载因子的 HashMap。通常情况下,加载因子使用默认的0.75就好。

初始容量

HashMap 的容量要求必须是2的N次幂,这样可以提高散列的均匀性,降低 Hash 冲突的风险。但是容量可以通过构造方法传入的,如果我传入一个非2次幂的数进去呢?比如3?传进去也不会干嘛呀,又不报错。。。哈哈哈哈。 是的,不会报错的,那是因为 HashMap 自己将这个数转成了一个最接近它的2次幂的数。这个转换的方法是 tableSizeFor(int cap) 。

/**
 * Returns a power of two size for the given target capacity.
 */
static final int tableSizeFor(int cap) {
		int n = cap - 1;
		n |= n >>> 1;
		n |= n >>> 2;
		n |= n >>> 4;
		n |= n >>> 8;
		n |= n >>> 16;
		return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

这个方法会将传入的数转换成一个2次幂的数,比如传入的是3,则返回的是4;传入12,则返回的是16。

加载因子

加载因子和 HashMap 的扩容机制有着非常重要的联系,它可以决定在什么时候才进行扩容。HashMap 是通过一个阀值来确定是否扩容,当容量超过这个阀值的时候就会进行扩容,而加载因子正是参与计算阀值的一个重要属性,阀值的计算公式是 容量 * 加载因子。如果通过默认构造方法创建 HashMap ,则阀值为 16 * 0.75 = 12 ,就是说当 HashMap 的容量超过12的时候就会进行扩容。

/**
 * The next size value at which to resize (capacity * load factor).
 *
 * @serial
 */
int threshold;

这是 HashMap 的 putVal(...) 方法的一个片段,put(...) 方法其实就是调用的这个方法,size 是当前 HashMap 的元素个数,当元素个数+1后超过了阀值就会调用 resize() 方法进行扩容。

if (++size > threshold)
		resize();

加载因子在一般情况下都最好不要去更改它,默认的0.75是一个非常科学的值,它是经过大量实践得出来的一个经验值。当加载因子设置的比较小的时候,阀值就会相应的变小,扩容次数就会变多,这就会导致 HashMap 的容量使用不充分,还没添加几个值进去就开始进行了扩容,浪费内存,扩容效率还很低;当加载因子设置的又比较大的时候呢,结果又很相反,阀值变大了,扩容次数少了,容量使用率又提高了,看上去是很不错,实际上还是有问题,因为容量使用的越多,Hash 冲突的概率就会越大。所以,选择一个合适的加载因子是非常重要的。

实践检验真理

默认无参构造方法测试

通过默认构造方法创建一个 HashMap,并循环添加13个值

HashMap的初始容量和加载因子

 

当添加第1个值后,容量为16,加载因子为0.75,阀值为12

HashMap的初始容量和加载因子

 

当添加完第13个值后,执行了扩容操作,容量变为了32,加载因子不变,阀值变为了24

HashMap的初始容量和加载因子

 

有参构造方法测试-只设置初始容量

创建一个初始容量为12(非2次幂数)的 HashMap,并添加1个值

HashMap的初始容量和加载因子

 

创建一个初始容量为2的 HashMap,并添加2个值

HashMap的初始容量和加载因子

 

当添加完第1个值后,容量为2,加载因子为0.75,阀值为1

HashMap的初始容量和加载因子

 

当添加完第2个值后,执行了扩容操作,容量变为4,加载因子为0.75,阀值为3

HashMap的初始容量和加载因子

 

有参构造方法测试-设置初始容量和加载因子

创建一个初始容量为2、加载因子为1的 HashMap,并添加2个值

HashMap的初始容量和加载因子

 

当添加完第1个值后,容量为2,加载因子为1,阀值为2

HashMap的初始容量和加载因子

 

当添加完第2个值后,并没有执行扩容操作,容量、加载因子、阀值均没有变化

HashMap的初始容量和加载因子

 



Tags:HashMap   点击:()  评论:()
声明:本站部分内容来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除,谢谢。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
jdk1.7中的底层实现过程(底层基于数组+链表)在我们new HashMap()时,底层创建了默认长度为16的一维数组Entry[ ] table。当我们调用map.put(key1,value1)方法向HashMap里添加数...【详细内容】
2020-06-28   HashMap  点击:(0)  评论:(0)  加入收藏
通过Map.values()遍历所有的value,不能遍历key public class TestDemo{ public static void main(String[] args) { HashMap<String,String> hashMap = new Has...【详细内容】
2020-03-08   HashMap  点击:(6)  评论:(0)  加入收藏
HashMap是数组+链表实现的,既然用到hash散列,那么肯定不可避免的会出现冲突问题,HashMap解决冲突的方法是拉链法,因为这里有用到数组,那么当容量不足的时候就需要进行扩容操作了...【详细内容】
2019-10-24   HashMap  点击:(21)  评论:(0)  加入收藏
介绍TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍...【详细内容】
2019-10-14   HashMap  点击:(151)  评论:(0)  加入收藏
一、HashMap1、基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相...【详细内容】
2019-10-10   HashMap  点击:(25)  评论:(0)  加入收藏
如何创建和初始化一个HashMap,看似简单的问题,其实真的挺简单的,但我们希望从多种方法中,选择一个最简洁方便、可读性也高的方法。 代码块初始化可以使用静态代码块和非静态代码...【详细内容】
2019-09-29   HashMap  点击:(992)  评论:(0)  加入收藏
注:本文所有代码示例均基于 JDK8。从源码出发默认值通过查看 HashMap 的源码可以得知其默认的初始容量为 16,默认的加载因子为 0.75。/** * The default initial capacity - M...【详细内容】
2019-09-09   HashMap  点击:(107)  评论:(0)  加入收藏
0. HashMap 简单说几句我们在学习 HashMap 的时候,都知道 HashMap 是非线程安全的,同时我们知道 HashTable 是线程安全的,因为里面的方法使用了 synchronized 进行同步。但是...【详细内容】
2019-09-03   HashMap  点击:(78)  评论:(0)  加入收藏
先来一张LinkedHashMap的结构图,不要虚,看完文章再来看这个图,就秒懂了,先混个面熟:...【详细内容】
2019-08-30   HashMap  点击:(42)  评论:(0)  加入收藏
1. HashMap概述:HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。...【详细内容】
2019-08-20   HashMap  点击:(45)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条