ArrayList内存增长策略和 removeIf
文章目录
- 增长策略
- removeIf(Predicate<? super E> filter)
增长策略
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
定义
intX // 传给构造函数的初始大小,int 值
collectionX // 传给构造函数的 collection
oldCapacity // 现有空间大小
minCapacity // oldCapacity + 要增加的元素个数
newCapacity // oldCapacity + (oldCapacity >> 1)
MAX_ARRAY_SIZE // 最大 int 值 - 8,- 8 是为了给一些 VM 预留 header words
ArrayList 从新建开始的增长大小如下表所示:
| 增长策略 | |
|---|---|
| new ArrayList() | 0 |
| new ArrayList(intX) | intX |
| new ArrayList(collectionX) | collectionX.size() |
| minCapacity <= 10 | 10 |
| newCapacity < minCapacity | minCapacity |
| newCapacity < MAX_ARRAY_SIZE | newCapacity |
| minCapacity <= MAX_ARRAY_SIZE | MAX_ARRAY_SIZE |
| minCapacity > MAX_ARRAY_SIZE | Integer.MAX_VALUE |
| minCapacity > Integer.MAX_VALUE | OutOfMemoryError |
removeIf(Predicate<? super E> filter)
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
这个方法是值得一说的。removeIf 这个需求,一听上去应该是遍历一次就可以的,而代码中先遍历一次用 bitSet 记录要改的位置,然后再一次遍历修改 List 中的数组。牺牲一次遍历的时间是为了防止 filter 条件执行时有异常,这样就会让 list 处于一个 inconsistent 的状态,所以这里采用两次遍历配合 bitSet 来保证数据的integrity
这次选读就到这里,有不对或者讲得不清楚的地方欢迎大家提出。