Reserve capacity in ProtobufArrayList when calling Builder.addAllRepeatedMessage(Collection)

This avoids some extra copies and garbage generation.

There's still the extra default 10-sized backing array created by default in
ProtobufArrayList which isn't fixed yet, which we see in allocation tests. That's from `ensureXIsMutable()` which allocates a new list without awareness of the list's size. Maybe we can fix that later: b/362848901.

PiperOrigin-RevId: 670766317
pull/17994/head
Mark Hansen 6 months ago committed by Copybara-Service
parent bd1887e436
commit e3cc31a12e
  1. 10
      java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
  2. 26
      java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java

@ -340,8 +340,14 @@ public abstract class AbstractMessageLite<
// We check nulls as we iterate to avoid iterating over values twice.
private static <T> void addAllCheckingNulls(Iterable<T> values, List<? super T> list) {
if (list instanceof ArrayList && values instanceof Collection) {
((ArrayList<T>) list).ensureCapacity(list.size() + ((Collection<T>) values).size());
if (values instanceof Collection) {
int growth = ((Collection<T>) values).size();
if (list instanceof ArrayList) {
((ArrayList<T>) list).ensureCapacity(list.size() + growth);
}
if (list instanceof ProtobufArrayList) {
((ProtobufArrayList<T>) list).ensureCapacity(list.size() + growth);
}
}
int begin = list.size();
if (values instanceof List && values instanceof RandomAccess) {

@ -52,8 +52,7 @@ final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements Rand
ensureIsMutable();
if (size == array.length) {
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
int length = growSize(size);
E[] newArray = Arrays.copyOf(array, length);
array = newArray;
@ -65,6 +64,11 @@ final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements Rand
return true;
}
private static int growSize(int previousSize) {
// Resize to 1.5x the size
return ((previousSize * 3) / 2) + 1;
}
@Override
public void add(int index, E element) {
ensureIsMutable();
@ -77,8 +81,7 @@ final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements Rand
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
} else {
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
int length = growSize(size);
E[] newArray = createArray(length);
// Copy the first part directly
@ -132,6 +135,21 @@ final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements Rand
return size;
}
/** Ensures the backing array can fit at least minCapacity elements. */
void ensureCapacity(int minCapacity) {
if (minCapacity <= array.length) {
return;
}
// To avoid quadratic copying when calling .addAllFoo(List) in a loop, we must not size to
// exactly the requested capacity, but must exponentially grow instead. This is similar
// behaviour to ArrayList.
int n = size;
while (n < minCapacity) {
n = growSize(n);
}
array = Arrays.copyOf(array, n);
}
@SuppressWarnings("unchecked")
private static <E> E[] createArray(int capacity) {
return (E[]) new Object[capacity];

Loading…
Cancel
Save