diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java index 15c783195d9b..c7816f88ae1c 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -340,8 +340,14 @@ private String getReadingExceptionMessage(String target) { // We check nulls as we iterate to avoid iterating over values twice. private static void addAllCheckingNulls(Iterable values, List list) { - if (list instanceof ArrayList && values instanceof Collection) { - ((ArrayList) list).ensureCapacity(list.size() + ((Collection) values).size()); + if (values instanceof Collection) { + int growth = ((Collection) values).size(); + if (list instanceof ArrayList) { + ((ArrayList) list).ensureCapacity(list.size() + growth); + } + if (list instanceof ProtobufArrayList) { + ((ProtobufArrayList) list).ensureCapacity(list.size() + growth); + } } int begin = list.size(); if (values instanceof List && values instanceof RandomAccess) { diff --git a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java index 68dba518aedb..e1d374ec648b 100644 --- a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java @@ -52,8 +52,7 @@ public boolean add(E element) { 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 @@ public boolean add(E element) { 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 @@ public void add(int index, E element) { // 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 @@ public int size() { 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[] createArray(int capacity) { return (E[]) new Object[capacity];