Skip to content

Commit

Permalink
Optimize ImmutableCollection.toArray(T[]) as per https://shipilev.net…
Browse files Browse the repository at this point in the history
…/blog/2016/arrays-wisdom-ancients/, the key goal being to avoid the necessity of zeroing a newly created array where possible.

Addresses #3209.

RELNOTES=n/a

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=207635563
  • Loading branch information
lowasser authored and ronshapiro committed Aug 9, 2018
1 parent bd3a471 commit f29f5d3
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 14 deletions.
Expand Up @@ -171,21 +171,20 @@ public abstract class ImmutableCollection<E> extends AbstractCollection<E> imple

@Override
public final Object[] toArray() {
int size = size();
if (size == 0) {
return EMPTY_ARRAY;
}
Object[] result = new Object[size];
copyIntoArray(result, 0);
return result;
return toArray(EMPTY_ARRAY);
}

@CanIgnoreReturnValue
@Override
public final <T> T[] toArray(T[] other) {
checkNotNull(other);
int size = size();

if (other.length < size) {
Object[] internal = internalArray();
if (internal != null) {
return Platform.copy(internal, internalArrayStart(), internalArrayEnd(), other);
}
other = ObjectArrays.newArray(other, size);
} else if (other.length > size) {
other[size] = null;
Expand All @@ -194,6 +193,27 @@ public final <T> T[] toArray(T[] other) {
return other;
}

/** If this collection is backed by an array of its elements in insertion order, returns it. */
Object[] internalArray() {
return null;
}

/**
* If this collection is backed by an array of its elements in insertion order, returns the offset
* where this collection's elements start.
*/
int internalArrayStart() {
throw new UnsupportedOperationException();
}

/**
* If this collection is backed by an array of its elements in insertion order, returns the offset
* where this collection's elements end.
*/
int internalArrayEnd() {
throw new UnsupportedOperationException();
}

@Override
public abstract boolean contains(@NullableDecl Object object);

Expand Down
15 changes: 15 additions & 0 deletions android/guava/src/com/google/common/collect/ImmutableList.java
Expand Up @@ -444,6 +444,21 @@ public int size() {
return length;
}

@Override
Object[] internalArray() {
return ImmutableList.this.internalArray();
}

@Override
int internalArrayStart() {
return ImmutableList.this.internalArrayStart() + offset;
}

@Override
int internalArrayEnd() {
return ImmutableList.this.internalArrayStart() + offset + length;
}

@Override
public E get(int index) {
checkElementIndex(index, length);
Expand Down
6 changes: 6 additions & 0 deletions android/guava/src/com/google/common/collect/Platform.java
Expand Up @@ -18,6 +18,7 @@

import com.google.common.annotations.GwtCompatible;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -86,6 +87,11 @@ static <T> T[] newArray(T[] reference, int length) {
return result;
}

/** Equivalent to Arrays.copyOfRange(source, from, to, arrayOfType.getClass()). */
static <T> T[] copy(Object[] source, int from, int to, T[] arrayOfType) {
return Arrays.copyOfRange(source, from, to, (Class<? extends T[]>) arrayOfType.getClass());
}

/**
* Configures the given map maker to use weak keys, if possible; does nothing otherwise (i.e., in
* GWT). This is sometimes acceptable, when only server-side code could generate enough volume
Expand Down
Expand Up @@ -65,6 +65,21 @@ int copyIntoArray(Object[] dst, int offset) {
return delegateList.copyIntoArray(dst, offset);
}

@Override
Object[] internalArray() {
return delegateList.internalArray();
}

@Override
int internalArrayStart() {
return delegateList.internalArrayStart();
}

@Override
int internalArrayEnd() {
return delegateList.internalArrayEnd();
}

@Override
public E get(int index) {
return delegateList.get(index);
Expand Down
Expand Up @@ -49,6 +49,21 @@ boolean isPartialView() {
return false;
}

@Override
Object[] internalArray() {
return array;
}

@Override
int internalArrayStart() {
return 0;
}

@Override
int internalArrayEnd() {
return size;
}

@Override
int copyIntoArray(Object[] dst, int dstOff) {
System.arraycopy(array, 0, dst, dstOff, size);
Expand Down
Expand Up @@ -74,6 +74,21 @@ public UnmodifiableIterator<E> iterator() {
return asList().iterator();
}

@Override
Object[] internalArray() {
return elements;
}

@Override
int internalArrayStart() {
return 0;
}

@Override
int internalArrayEnd() {
return size;
}

@Override
int copyIntoArray(Object[] dst, int offset) {
System.arraycopy(elements, 0, dst, offset, size);
Expand Down
Expand Up @@ -49,6 +49,21 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> {
this.elements = elements;
}

@Override
Object[] internalArray() {
return elements.internalArray();
}

@Override
int internalArrayStart() {
return elements.internalArrayStart();
}

@Override
int internalArrayEnd() {
return elements.internalArrayEnd();
}

@Override
public UnmodifiableIterator<E> iterator() {
return elements.iterator();
Expand Down
Expand Up @@ -89,6 +89,28 @@ ImmutableList<E> createAsList() {
}
}

/** If this collection is backed by an array of its elements in insertion order, returns it. */
@Nullable
Object[] internalArray() {
return null;
}

/**
* If this collection is backed by an array of its elements in insertion order, returns the offset
* where this collection's elements start.
*/
int internalArrayStart() {
throw new UnsupportedOperationException();
}

/**
* If this collection is backed by an array of its elements in insertion order, returns the offset
* where this collection's elements end.
*/
int internalArrayEnd() {
throw new UnsupportedOperationException();
}

static <E> ImmutableCollection<E> unsafeDelegate(Collection<E> delegate) {
return new ForwardingImmutableCollection<E>(delegate);
}
Expand Down
Expand Up @@ -73,6 +73,13 @@ private static void resizeArray(Object array, int newSize) {
((NativeArray) array).setLength(newSize);
}

/** Equivalent to Arrays.copyOfRange(source, from, to, arrayOfType.getClass()). */
static <T> T[] copy(Object[] source, int from, int to, T[] arrayOfType) {
T[] result = newArray(arrayOfType, to - from);
System.arraycopy(source, from, result, 0, to - from);
return result;
}

// TODO(user): Move this logic to a utility class.
@JsType(isNative = true, name = "Array", namespace = JsPackage.GLOBAL)
private interface NativeArray {
Expand Down
35 changes: 28 additions & 7 deletions guava/src/com/google/common/collect/ImmutableCollection.java
Expand Up @@ -183,21 +183,20 @@ public Spliterator<E> spliterator() {

@Override
public final Object[] toArray() {
int size = size();
if (size == 0) {
return EMPTY_ARRAY;
}
Object[] result = new Object[size];
copyIntoArray(result, 0);
return result;
return toArray(EMPTY_ARRAY);
}

@CanIgnoreReturnValue
@Override
public final <T> T[] toArray(T[] other) {
checkNotNull(other);
int size = size();

if (other.length < size) {
Object[] internal = internalArray();
if (internal != null) {
return Platform.copy(internal, internalArrayStart(), internalArrayEnd(), other);
}
other = ObjectArrays.newArray(other, size);
} else if (other.length > size) {
other[size] = null;
Expand All @@ -206,6 +205,28 @@ public final <T> T[] toArray(T[] other) {
return other;
}

/** If this collection is backed by an array of its elements in insertion order, returns it. */
@Nullable
Object[] internalArray() {
return null;
}

/**
* If this collection is backed by an array of its elements in insertion order, returns the offset
* where this collection's elements start.
*/
int internalArrayStart() {
throw new UnsupportedOperationException();
}

/**
* If this collection is backed by an array of its elements in insertion order, returns the offset
* where this collection's elements end.
*/
int internalArrayEnd() {
throw new UnsupportedOperationException();
}

@Override
public abstract boolean contains(@Nullable Object object);

Expand Down
6 changes: 6 additions & 0 deletions guava/src/com/google/common/collect/Platform.java
Expand Up @@ -18,6 +18,7 @@

import com.google.common.annotations.GwtCompatible;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -86,6 +87,11 @@ static <T> T[] newArray(T[] reference, int length) {
return result;
}

/** Equivalent to Arrays.copyOfRange(source, from, to, arrayOfType.getClass()). */
static <T> T[] copy(Object[] source, int from, int to, T[] arrayOfType) {
return Arrays.copyOfRange(source, from, to, (Class<? extends T[]>) arrayOfType.getClass());
}

/**
* Configures the given map maker to use weak keys, if possible; does nothing otherwise (i.e., in
* GWT). This is sometimes acceptable, when only server-side code could generate enough volume
Expand Down
15 changes: 15 additions & 0 deletions guava/src/com/google/common/collect/RegularImmutableAsList.java
Expand Up @@ -68,6 +68,21 @@ int copyIntoArray(Object[] dst, int offset) {
return delegateList.copyIntoArray(dst, offset);
}

@Override
Object[] internalArray() {
return delegateList.internalArray();
}

@Override
int internalArrayStart() {
return delegateList.internalArrayStart();
}

@Override
int internalArrayEnd() {
return delegateList.internalArrayEnd();
}

@Override
public E get(int index) {
return delegateList.get(index);
Expand Down
15 changes: 15 additions & 0 deletions guava/src/com/google/common/collect/RegularImmutableList.java
Expand Up @@ -47,6 +47,21 @@ boolean isPartialView() {
return false;
}

@Override
Object[] internalArray() {
return array;
}

@Override
int internalArrayStart() {
return 0;
}

@Override
int internalArrayEnd() {
return array.length;
}

@Override
int copyIntoArray(Object[] dst, int dstOff) {
System.arraycopy(array, 0, dst, dstOff, array.length);
Expand Down
15 changes: 15 additions & 0 deletions guava/src/com/google/common/collect/RegularImmutableSet.java
Expand Up @@ -79,6 +79,21 @@ public Spliterator<E> spliterator() {
return Spliterators.spliterator(elements, SPLITERATOR_CHARACTERISTICS);
}

@Override
Object[] internalArray() {
return elements;
}

@Override
int internalArrayStart() {
return 0;
}

@Override
int internalArrayEnd() {
return elements.length;
}

@Override
int copyIntoArray(Object[] dst, int offset) {
System.arraycopy(elements, 0, dst, offset, elements.length);
Expand Down
15 changes: 15 additions & 0 deletions guava/src/com/google/common/collect/RegularImmutableSortedSet.java
Expand Up @@ -50,6 +50,21 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> {
this.elements = elements;
}

@Override
Object[] internalArray() {
return elements.internalArray();
}

@Override
int internalArrayStart() {
return elements.internalArrayStart();
}

@Override
int internalArrayEnd() {
return elements.internalArrayEnd();
}

@Override
public UnmodifiableIterator<E> iterator() {
return elements.iterator();
Expand Down

0 comments on commit f29f5d3

Please sign in to comment.