当第一眼看到这个问题,大脑下意识的会想到 ArrayList
集合的的 初始容量和 扩容机制;想到再深一点的就会联想到HashMap的初始容量和扩容机制。
但坑,就坑在这点。当你下意识想到 ArrayList 怎么进行扩容时,就已经陷入了这道题的陷阱了。
一想到扩容,就会对比 ArrayList 的初始容量,初始容量是10,然后每次需要扩容的时候就按照当前容量的 1.5 倍来分配新的空间,也就是 扩容后是原来容量的1.5倍。接着 20 比 10大,按照10*1.5=15,15 * 1.5 取 22 个,是不是就需要扩容两次?错,大错特错。
每次按照1.5倍(位运算)的比率通过copeOf的方式扩容。以上就是动态扩容的原理。
我们通常讲集合扩容,往往会忽略一个前提,就是对集合空间进行扩容的前提是:集合中的已经存储的元素个数达到了扩容的条件,然后再去触发扩容机制。
那回到 ArrayList。这里就讲 jdk1.7之后。
New ArrayList():
是什么?调用的 无参构造 方法,创建一个空的集合,这个过程称为“初始化”。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这是无参构造,默认赋值初始容量的操作,elementData就是用来处理值的扩容操作等等。
这时候属于没有指定某一个空间大小,jdk会给一个默认的初始容量,也就是 0。
在没有往空的集合中添加元素之前,它分配的空间大小是 0;
当第一次插入元素时才给分配10(默认)个对象空间。当添加第16个数据时,触发扩容机制,继续扩容变为15 * 1.5 =22个;
New ArrayList(20);
是什么?调用 有参构造 方法,创建一个 容量为20 的集合,这个过程也称为“初始化”。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
这就是直接定义好了初始容量20,此时也并没有元素,更没有达到触发扩容条件需要的元素个数。所以也就没有发生扩容。
List<String> list = new ArrayList<>(11);
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
list.add("1");
// 此时给 list 集合的11个空间中存储了元素.当再次调用 add()方法添加元素时,就会触发扩容机制
// 触发扩容机制后,11*1.5 取 16。此时给list分配的空间大小为 16,然后 将要添加的第 12 个元素就放在索引为11的位置
list.add("12");
JDK1.7以后ArrayList list =new ArrayList() 无参构造的话,初始数组容量为0的,在真正添加数据到数组里,才会调用add()/addAll()方法并真正分配其容量,按照1.5的比率去扩容。就是初始10个元素容量的数组,当给到第11个数据时,就会扩容到15,22等等。 就是按Arrays.copyOf(elementData, newCapacity)这个方式去实现的。