一、内部数组大小与初始容量的关系
1、什么是初始容量?
初始容量指的是ArrayList在创建时分配的内部数组大小,也就是能够存储元素的空间大小。通常情况下我们使用不带参数的构造方法来创建ArrayList,这种情况下,初始容量为10。但是,如果我们有提前知道ArrayList中会存储的元素数量,那么在创建时可以通过传入一个整数值来指定初始容量。这样,初始化时就能够自动设置内部数组的大小。
2、内部数组的实际大小是多少?
除非使用带有参数构造函数来指定初始容量,否则ArrayList的内部数组大小始终为10。但是,这个初始容量只是初始值,实际上ArrayList会自动扩容。
3、什么时候会触发自动扩容?
当我们往ArrayList中添加超出容量的元素时,就会触发ArrayList自动扩容。具体来说,它会把当前的内部数组复制到一个更大的数组中,然后继续添加新元素。
// 通过构造函数指定初始容量
ArrayList list = new ArrayList(20);
// 内部数组大小是多少?
System.out.println(list.size()); // 输出 0
二、初始容量与性能的关系
1、为什么初始容量重要?
初始容量实际上在ArrayList的性能中扮演着重要的角色。不同的初始容量会影响到ArrayList的性能表现。如果初始容量太小,ArrayList就会频繁地进行扩容操作,影响效率。而如果初始容量太大,将导致内存浪费。
2、如何确定合适的初始容量?
为了避免频繁扩容,我们需要给出一个合适的初始容量。通常,初始容量应该尽可能地接近预期的元素个数以减少内存浪费。一般来说,我们可以通过创建一个稍大于实际元素个数的ArrayList,然后调整其初始容量来实现优化。
3、通过代码测试ArrayList在不同初始容量下的性能:
// 不同的初始容量对添加性能的影响
int size = 1000000;
long start = System.currentTimeMillis();
ArrayList list1 = new ArrayList(size);
for (int i = 0; i < size; i++) {
list1.add(i);
}
long end = System.currentTimeMillis();
System.out.println("指定初始容量:" + (end - start) + " 毫秒");
start = System.currentTimeMillis();
ArrayList list2 = new ArrayList();
for (int i = 0; i < size; i++) {
list2.add(i);
}
end = System.currentTimeMillis();
System.out.println("未指定初始容量:" + (end - start) + " 毫秒");
三、初始容量调整的实现原理
1、如何调整初始容量?
调整初始容量的核心方法是ensureCapacity()
,通过调用这个方法来设置ArrayList的实际容量。如果我们知道ArrayList中需要多少元素,可以在创建之时指定容量。否则,在添加元素时,每当容量不足时,ArrayList就会自动增加一个“增量”来调整容量。默认情况下,“增量”是当前容量的一半。
2、如何自定义“增量”?
我们可以通过设置ArrayList(int initialCapacity)
构造方法中的增量参数来自定义“增量”值。如果我们希望增量增加一倍或者减半,那么只需在创建ArrayList对象时传递一个新的增量参数即可。
3、添加元素时自动调整容量的实现原理:
// 添加元素时自动增加容量的实现原理
private static final int DEFAULT_CAPACITY = 10;
private Object[] elementData;
private int size;
private void ensureCapacity(int capacity) {
if (capacity > elementData.length) {
// 计算新长度
int newCapacity = elementData.length + (elementData.length >> 1);
// 如果扩增容量还是不够,就直接扩大容量到需要的大小
if (newCapacity < capacity) {
newCapacity = capacity;
}
// 复制原数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
public boolean add(E e) {
// 添加元素时自动扩容
ensureCapacity(size + 1);
elementData[size] = e;
size++;
return true;
}
四、初始容量与内存开销的关系
1、如何减少内存开销?
随着初始容量的增大,ArrayList占用的内存空间也会增多。因此,如果我们想要降低内存开销,就需要尽可能地减少初始容量。
2、如何查看ArrayList占用的内存空间?
我们可以通过在Java应用程序中使用JProfiler等内存分析工具来查看ArrayList实例占用的内存空间。
3、代码示例:如何比较不同初始容量的内存占用情况:
// 不同初始容量下内存占用的差异
Runtime runtime = Runtime.getRuntime();
long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
ArrayList list = new ArrayList();
for (int i = 0; i < 100000; i++) {
list.add("Element " + i);
}
long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("内存占用:" + (usedMemoryAfter - usedMemoryBefore) + " bytes");
usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
list = new ArrayList(100000);
for (int i = 0; i < 100000; i++) {
list.add("Element " + i);
}
usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("内存占用:" + (usedMemoryAfter - usedMemoryBefore) + " bytes");
五、总结
本文从各个方面深入阐述了ArrayList的初始容量问题,包括内部数组大小与初始容量的关系、初始容量与性能的关系、初始容量调整的实现原理、初始容量与内存开销的关系。通过深入研究ArrayList的初始容量问题,我们可以更好地使用它,避免无谓的内存浪费,优化程序性能。
原创文章,作者:TNHOT,如若转载,请注明出处:https://www.506064.com/n/329021.html