ArrayList、HashMap扩容
1.ArrayList扩容
默认容量是10,如果初始化时一开始指定了容量,或者通过集合作为元素,则容量为指定的大小或参数集合的大小。每次扩容为原来的1.5倍,如果新增后超过这个容量,则容量为新增后所需的最小容量。如果增加0.5倍后的新容量超过限制的容量,则用所需的最小容量与限制的容量进行判断,超过则指定为Integer的最大值,否则指定为限制容量大小。然后通过数组的复制将原数据复制到一个更大(新的容量大小)的数组。
2.HashMap扩容
HashMap扩容可以分为三种情况:
什么时候扩容:当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值---即当前数组的长度乘以加载因子的值的时候,就要自动扩容啦。
扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用 一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。
HashMap中的变量
首先要了解HashMap的扩容过程,我们就得了解一些HashMap中的变量:
- Node<K,V>:链表节点,包含了key、value、hash、next指针四个元素
- table:Node<K,V>类型的数组,里面的元素是链表,用于存放HashMap元素的实体
- size:记录了放入HashMap的元素个数
- loadFactor:负载因子
- threshold:阈值,决定了HashMap何时扩容,以及扩容后的大小,一般等于table大小乘以loadFactor
HashMap的构造函数
HashMap的构造函数主要有四个,代码如下:
- public HashMap(int initialCapacity, float loadFactor) {
- ...
- this.loadFactor = loadFactor;
- this.threshold = tableSizeFor(initialCapacity);
- }
- public HashMap(int initialCapacity) {
- this(initialCapacity, DEFAULT_LOAD_FACTOR);
- }
- public HashMap() {
- this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
- }
- public HashMap(Map<? extends K, ? extends V> m) {
- this.loadFactor = DEFAULT_LOAD_FACTOR;
- putMapEntries(m, false);
- }
其中主要有两种形式:
- 直接拷贝别的HashMap的形式,在此不作讨论
- 定义初始容量大小(table数组的大小,缺省值为16),定义负载因子(缺省值为0.75)的形式
值得注意的是,当我们自定义HashMap初始容量大小时,构造函数并非直接把我们定义的数值当做HashMap容量大小,而是把该数值当做参数调用方法tableSizeFor,然后把返回值作为HashMap的初始容量大小:
- /**
- * 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的倍数,因此HashMap中的table数组的容量大小总是2的倍数。