验证 ArrayList 的扩容机制
背景
可以调用 java.util.ArrayList#add(E) 方法来向 ArrayList 中添加元素。
这个方法的代码如下
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
它调用了 java.util.ArrayList#add(E, java.lang.Object[], int) 方法(代码如下)。
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
而 java.util.ArrayList#grow() 方法的代码如下。
private Object[] grow() {
return grow(size + 1);
}
java.util.ArrayList#grow(int) 的代码如下。
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
我们可以写一段代码来分析 ArrayList 具体是如何扩容的。
通过反射来观察扩容的过程
请将以下代码保存为 ArrayListAnalyzer.java。
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class ArrayListAnalyzer {
private final List<String> strings;
private final Field elementDataField;
private final Field sizeField;
private ArrayListAnalyzer(List<String> strings) throws NoSuchFieldException, IllegalAccessException {
this.strings = strings;
elementDataField = ArrayList.class.getDeclaredField("elementData");
sizeField = ArrayList.class.getDeclaredField("size");
elementDataField.setAccessible(true);
sizeField.setAccessible(true);
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
List<String> strings = new ArrayList<>();
ArrayListAnalyzer analyzer = new ArrayListAnalyzer(strings);
int oldCapacity = analyzer.getCapacity();
int oldSize = analyzer.getSize();
for (int i = 0; i < 1000; i++) {
strings.add("");
int newCapacity = analyzer.getCapacity();
int newSize = analyzer.getSize();
if (newCapacity != oldCapacity) {
System.out.printf("When size changes from %s to %s, capacity changes from %s to %s accordingly.%n",
oldSize,
newSize,
oldCapacity,
newCapacity
);
}
oldCapacity = newCapacity;
oldSize = newSize;
}
}
private int getCapacity() throws IllegalAccessException {
Object[] elementData = ((Object[]) elementDataField.get(strings));
return elementData.length;
}
private int getSize() throws IllegalAccessException {
return (int) sizeField.get(strings);
}
}
以下命令可以编译 ArrayListAnalyzer.java。
javac ArrayListAnalyzer.java
以下命令可以运行 ArrayListAnalyzer 类中的 main 方法。
java --add-opens=java.base/java.util=ALL-UNNAMED ArrayListAnalyzer
运行结果如下。
When size changes from 0 to 1, capacity changes from 0 to 10 accordingly.
When size changes from 10 to 11, capacity changes from 10 to 15 accordingly.
When size changes from 15 to 16, capacity changes from 15 to 22 accordingly.
When size changes from 22 to 23, capacity changes from 22 to 33 accordingly.
When size changes from 33 to 34, capacity changes from 33 to 49 accordingly.
When size changes from 49 to 50, capacity changes from 49 to 73 accordingly.
When size changes from 73 to 74, capacity changes from 73 to 109 accordingly.
When size changes from 109 to 110, capacity changes from 109 to 163 accordingly.
When size changes from 163 to 164, capacity changes from 163 to 244 accordingly.
When size changes from 244 to 245, capacity changes from 244 to 366 accordingly.
When size changes from 366 to 367, capacity changes from 366 to 549 accordingly.
When size changes from 549 to 550, capacity changes from 549 to 823 accordingly.
When size changes from 823 to 824, capacity changes from 823 to 1234 accordingly.