1700443390
1700443391
super();
1700443392
1700443393
if(initialCapacity<0)
1700443394
1700443395
throw new IllegalArgumentException(“Illegal Capacity:”+initialCapacity);
1700443396
1700443397
//声明指定长度的数组,容纳element
1700443398
1700443399
this.elementData=new Object[initialCapacity];
1700443400
1700443401
}
1700443402
1700443403
默认初始化时声明了一个长度为10的数组,在通过add方法增加第11个元素时,ArrayList类就自动扩展了,新的elementData数组长度是(10×3)/2+1,也就是16,当增加到第17个元素时再次扩容为(16×3)/2+1,也就是25,依此类推,实现了ArrayList的动态数组管理。
1700443404
1700443405
从这里我们可以看出,如果不设置初始容量,系统就按照1.5倍的规则扩容,每次扩容都是一次数组的拷贝,如果数据量很大,这样的拷贝会非常耗费资源,而且效率非常低下。如果我们已经知道一个ArrayList的可能长度,然后对ArrayList设置一个初始容量则可以显著提高系统性能。比如一个班级的学生,通常也就是50人左右,我们就声明ArrayList的默认容量为50的1.5倍(元素数量小,直接计算,避免数组拷贝),即new ArrayList<Studeng>(75),这样在使用add方法增加元素时,只要在75以内都不用做数组拷贝,超过了75才会按照默认规则扩容(也就是1.5倍扩容)。如此处理,对我们的开发逻辑并不会有任何影响,而且还可以提高运行效率(在大数据量下,是否指定容量会使性能相差5倍以上)。
1700443406
1700443407
弄明白了ArrayList的长度处理方式,那其他集合类型呢?我们先来看Vector,它的处理方式与ArrayList相似,只是数组的长度计算方式不同而已,代码如下:
1700443408
1700443409
private void ensureCapacityHelper(int minCapacity){
1700443410
1700443411
int oldCapacity=elementData.length;
1700443412
1700443413
if(minCapacity>oldCapacity){
1700443414
1700443415
Object[]oldData=elementData;
1700443416
1700443417
//若有递增步长,则按照步长增长;否则,扩容2倍
1700443418
1700443419
int newCapacity=(capacityIncrement>0)?(oldCapacity+capacityIncrement)
1700443420
1700443421
:(oldCapacity*2);
1700443422
1700443423
//越界检查,否则超过int最大值
1700443424
1700443425
if(newCapacity<minCapacity){
1700443426
1700443427
newCapacity=minCapacity;
1700443428
1700443429
}
1700443430
1700443431
elementData=Arrays.copyOf(elementData, newCapacity);
1700443432
1700443433
}
1700443434
1700443435
}
1700443436
1700443437
Vector与ArrayList不同的地方是它提供了递增步长(capacityIncrement变量),其值代表的是每次数组拓长时要增加的长度,不设置此值则是容量翻倍(默认是不设置递增步长的,可以通过构造函数来设置递增步长)。其他集合类的扩容方式与此相似,如HashMap是按照倍数增加的,Stack继承自Vector,所采用的也是与其相同的扩容原则等,读者有兴趣可以自行研读一下JDK的源码。
1700443438
1700443439
注意 非常有必要在集合初始化时声明容量。
[
上一页 ]
[ :1.70044339e+09 ]
[
下一页 ]