文章目录
前言
在之前提到多数据存储的三种方式:数组、链表、树,它们单独使用时都非常不方便,因此存在一些集合接口提高了多数据存储的能力。
集合分为单列集合和双列集合,其下的一些实现类都存在自己的扩容机制,本篇介绍其扩容机制
一、单列集合List
1.1 ArrayList
由jdk1.8 底层源码可知,默认情况下加载因子是1,且默认容量为10,存在自动扩容机制
- 默认加载因子:当元素个数超过 容量长度*加载因子的系数时,进行扩容。
即ArrayList中,元素个数>原长度的一倍(即原长度),就进行扩容
- 通过ArrayList中的grow()方法及其他方法进行自动扩容的操作
且不同jdk版本中grow()位置可能不一样
- 存在的问题:
- ArrayList其实长度有局限,如果超出长度的情况下,需要内部进行扩容操作,只要是扩容,就会出现垃圾空间
- 通过无参构造创建集合,第一次存值的时候就要进行扩容操作,意味着垃圾空间的产生
- 扩容的基本量是原始的1/2,然后把之前的数据拷贝到新建的数组中去
- 因此在ArrayList进行定义时,如果暂时不使用,就定义长度为0;如果立马进行添加操作,要添加几个就定义多少长度
进行一个预估长度,避免出现ArrayList进行自动扩容时产生多余的垃圾空间
1.2 Vector
由jdk1.8 底层源码可知,默认容量为10,存在自动扩容机制
-
Vector默认情况下加载因子是1,扩容后的容量是之前的2倍(未设置容量增量时),超过即自动扩容
-
Vector扩容时可以不用默认扩容的容量,即可以设置容量增量,而ArrayList不可以
1.3 Stack
Stack栈继承自Vector。添加了同步的push(E e),pop(),peek(),search()方法,默认容量和扩容机制同Vector
-
Stack默认容量是10,加载因子: 1
-
Stack扩容机制: 如果用户没有指定扩容步长,按原数组长度的2倍扩容,否则按用户指定的扩容步长扩容。
如果扩容后的大小小于实际需要的大小,将数组扩大到实际需要的大小
1.3 LinkedList
没有扩容机制,因为其底层是双向链表结构。不存在数组的扩容一说,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。
二、单列集合Set
2.1 HashSet
由jdk1.8 底层源码可知,默认加载因子为0.75,用来判断扩容的时机,因此存在自动扩容机制
- 默认容量16,加载因子0.75,超出后按照1倍扩容
2.2 LinkedHashSet
- 默认容量16,加载因子0.75,超出后按照2倍扩容
2.3 TreeSet
- 由源代码可知,创建TreeSet对象时是构造一个空树集,底层原理是红黑树,因此不存在扩容机制
三、双列集合Map
3.1 HashMap
jdk1.8默认初始容量0,当第一次put元素的时候才扩容为16,jdk1.7是初始化容量为16,即默认容量16,加载因子0.75,超出后按照1倍扩容(最大容量为1<< 30)
- map如果只提供初始空间大小,每次扩充的值为默认的75%
如果提供扩充阈值,则按照该阈值扩充
3.2 Hashtable
扩容加载因子(0.75),当超出默认长度(int)(11*0.75)=8时,扩容为oldx2+1。新容量为原容量的2倍+1.
3.3 LinkedHashMap
LinkedHashMap的扩容机制 LinkedHashMap的扩容机制与HashMap类似。
- 当LinkedHashMap的元素数量超过负载因子(默认为0.75)与容量的乘积时,会触发扩容操作。 扩容操作会将容量扩大为原来的两倍,并重新计算每个元素在新容量下的位置。
- 在扩容过程中,原来的元素顺序会被保留,因为LinkedHashMap是基于链表和哈希表实现的
3.4 TreeMap
当存储在Treemap中的元素数量达到当前容量的75%时,Treemap会自动进行扩容操作。
- 进行扩容时,Treemap会创建一个新的红黑树,新树的容量是原来树容量的两倍。
- 将原树中的所有元素按照它们的key值重新插入到新树中。
由于红黑树的插入操作的时间复杂度是O (logN),这个过程会有一定的时间代价。 - 扩容后,原来的红黑树会被回收释放空间