8156071: List.of: reduce array copying during creation

Reviewed-by: psandoz, redestad
This commit is contained in:
Stuart Marks 2020-10-05 17:00:57 +00:00
parent ea27a54bf0
commit 88d75c9ad5
6 changed files with 203 additions and 18 deletions

View file

@ -36,6 +36,7 @@ import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import jdk.internal.access.JavaUtilCollectionAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.Stable;
@ -103,7 +104,7 @@ class ImmutableCollections {
CDS.initializeFromArchive(ImmutableCollections.class);
if (archivedObjects == null) {
EMPTY = new Object();
EMPTY_LIST = new ListN<>();
EMPTY_LIST = new ListN<>(new Object[0]);
EMPTY_SET = new SetN<>();
EMPTY_MAP = new MapN<>();
archivedObjects = new Object[] { EMPTY, EMPTY_LIST, EMPTY_SET, EMPTY_MAP };
@ -115,6 +116,16 @@ class ImmutableCollections {
}
}
static class Access {
static {
SharedSecrets.setJavaUtilCollectionAccess(new JavaUtilCollectionAccess() {
public <E> List<E> listFromTrustedArray(Object[] array) {
return ImmutableCollections.ListN.fromTrustedArray(array);
}
});
}
}
/** No instances. */
private ImmutableCollections() { }
@ -521,15 +532,31 @@ class ImmutableCollections {
@Stable
private final E[] elements;
private ListN(E[] array) {
elements = array;
}
// creates a new internal array, and checks and rejects null elements
@SafeVarargs
ListN(E... input) {
static <E> List<E> fromArray(E... input) {
// copy and check manually to avoid TOCTOU
@SuppressWarnings("unchecked")
E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
for (int i = 0; i < input.length; i++) {
tmp[i] = Objects.requireNonNull(input[i]);
}
elements = tmp;
return new ListN<>(tmp);
}
// Avoids creating a new array, but checks and rejects null elements.
// Declared with Object... arg so that varargs calls don't accidentally
// create an array of a subtype.
@SuppressWarnings("unchecked")
static <E> List<E> fromTrustedArray(Object... input) {
for (Object o : input) {
Objects.requireNonNull(o);
}
return new ListN<>((E[])input);
}
@Override

View file

@ -842,7 +842,7 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3) {
return new ImmutableCollections.ListN<>(e1, e2, e3);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3);
}
/**
@ -861,7 +861,7 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4);
}
/**
@ -881,7 +881,7 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5);
}
/**
@ -902,8 +902,8 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
e6);
}
/**
@ -925,8 +925,8 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
e6, e7);
}
/**
@ -949,8 +949,8 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7, e8);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8);
}
/**
@ -974,8 +974,8 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7, e8, e9);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9);
}
/**
@ -1000,8 +1000,8 @@ public interface List<E> extends Collection<E> {
* @since 9
*/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
}
/**
@ -1042,7 +1042,7 @@ public interface List<E> extends Collection<E> {
case 2:
return new ImmutableCollections.List12<>(elements[0], elements[1]);
default:
return new ImmutableCollections.ListN<>(elements);
return ImmutableCollections.ListN.fromArray(elements);
}
}

View file

@ -56,6 +56,8 @@ import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import jdk.internal.access.SharedSecrets;
/**
* Implementations of {@link Collector} that implement various useful reduction
* operations, such as accumulating elements into collections, summarizing
@ -296,7 +298,8 @@ public final class Collectors {
Collector<T, ?, List<T>> toUnmodifiableList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
list -> (List<T>)List.of(list.toArray()),
list -> (List<T>)SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArray(list.toArray()),
CH_NOID);
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.access;
import java.util.List;
public interface JavaUtilCollectionAccess {
<E> List<E> listFromTrustedArray(Object[] array);
}

View file

@ -70,6 +70,7 @@ public class SharedSecrets {
private static JavaNetUriAccess javaNetUriAccess;
private static JavaNetURLAccess javaNetURLAccess;
private static JavaNioAccess javaNioAccess;
private static JavaUtilCollectionAccess javaUtilCollectionAccess;
private static JavaUtilJarAccess javaUtilJarAccess;
private static JavaUtilZipFileAccess javaUtilZipFileAccess;
private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess;
@ -77,6 +78,19 @@ public class SharedSecrets {
private static JavaSecuritySignatureAccess javaSecuritySignatureAccess;
private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess;
public static void setJavaUtilCollectionAccess(JavaUtilCollectionAccess juca) {
javaUtilCollectionAccess = juca;
}
public static JavaUtilCollectionAccess getJavaUtilCollectionAccess() {
if (javaUtilCollectionAccess == null) {
try {
Class.forName("java.util.ImmutableCollections$Access", true, null);
} catch (ClassNotFoundException e) {};
}
return javaUtilCollectionAccess;
}
public static JavaUtilJarAccess javaUtilJarAccess() {
if (javaUtilJarAccess == null) {
// Ensure JarFile is initialized; we know that this class