8060192: Add default method <A> A[] Collection.toArray(IntFunction<A[]> generator)
Reviewed-by: martin, forax, psandoz, briangoetz
--- a/src/java.base/share/classes/java/util/Collection.java Thu Jun 21 11:10:44 2018 -0400
+++ b/src/java.base/share/classes/java/util/Collection.java Thu Jun 21 08:25:03 2018 -0700
@@ -25,6 +25,7 @@
package java.util;
+import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -276,8 +277,12 @@
* allocate a new array even if this collection is backed by an array).
* The caller is thus free to modify the returned array.
*
- * <p>This method acts as bridge between array-based and collection-based
- * APIs.
+ * @apiNote
+ * This method acts as a bridge between array-based and collection-based APIs.
+ * It returns an array whose runtime type is {@code Object[]}.
+ * Use {@link #toArray(Object[]) toArray(T[])} to reuse an existing
+ * array, or use {@link #toArray(IntFunction)} to control the runtime type
+ * of the array.
*
* @return an array, whose {@linkplain Class#getComponentType runtime component
* type} is {@code Object}, containing all of the elements in this collection
@@ -302,19 +307,27 @@
* are returned by its iterator, this method must return the elements in
* the same order.
*
- * <p>Like the {@link #toArray()} method, this method acts as bridge between
- * array-based and collection-based APIs. Further, this method allows
- * precise control over the runtime type of the output array, and may,
- * under certain circumstances, be used to save allocation costs.
+ * @apiNote
+ * This method acts as a bridge between array-based and collection-based APIs.
+ * It allows an existing array to be reused under certain circumstances.
+ * Use {@link #toArray()} to create an array whose runtime type is {@code Object[]},
+ * or use {@link #toArray(IntFunction)} to control the runtime type of
+ * the array.
*
* <p>Suppose {@code x} is a collection known to contain only strings.
- * The following code can be used to dump the collection into a newly
- * allocated array of {@code String}:
+ * The following code can be used to dump the collection into a previously
+ * allocated {@code String} array:
*
* <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * String[] y = new String[SIZE];
+ * ...
+ * y = x.toArray(y);</pre>
*
- * Note that {@code toArray(new Object[0])} is identical in function to
+ * <p>The return value is reassigned to the variable {@code y}, because a
+ * new array will be allocated and returned if the collection {@code x} has
+ * too many elements to fit into the existing array {@code y}.
+ *
+ * <p>Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
*
* @param <T> the component type of the array to contain the collection
@@ -329,6 +342,45 @@
*/
<T> T[] toArray(T[] a);
+ /**
+ * Returns an array containing all of the elements in this collection,
+ * using the provided {@code generator} function to allocate the returned array.
+ *
+ * <p>If this collection makes any guarantees as to what order its elements
+ * are returned by its iterator, this method must return the elements in
+ * the same order.
+ *
+ * @apiNote
+ * This method acts as a bridge between array-based and collection-based APIs.
+ * It allows creation of an array of a particular runtime type. Use
+ * {@link #toArray()} to create an array whose runtime type is {@code Object[]},
+ * or use {@link #toArray(Object[]) toArray(T[])} to reuse an existing array.
+ *
+ * <p>Suppose {@code x} is a collection known to contain only strings.
+ * The following code can be used to dump the collection into a newly
+ * allocated array of {@code String}:
+ *
+ * <pre>
+ * String[] y = x.toArray(String[]::new);</pre>
+ *
+ * @implSpec
+ * The default implementation calls the generator function with zero
+ * and then passes the resulting array to {@link #toArray(Object[]) toArray(T[])}.
+ *
+ * @param <T> the component type of the array to contain the collection
+ * @param generator a function which produces a new array of the desired
+ * type and the provided length
+ * @return an array containing all of the elements in this collection
+ * @throws ArrayStoreException if the runtime type of any element in this
+ * collection is not assignable to the {@linkplain Class#getComponentType
+ * runtime component type} of the generated array
+ * @throws NullPointerException if the generator function is null
+ * @since 11
+ */
+ default <T> T[] toArray(IntFunction<T[]> generator) {
+ return toArray(generator.apply(0));
+ }
+
// Modification Operations
/**
--- a/src/java.base/share/classes/java/util/Collections.java Thu Jun 21 11:10:44 2018 -0400
+++ b/src/java.base/share/classes/java/util/Collections.java Thu Jun 21 08:25:03 2018 -0700
@@ -33,6 +33,7 @@
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.IntStream;
@@ -1028,12 +1029,13 @@
this.c = c;
}
- public int size() {return c.size();}
- public boolean isEmpty() {return c.isEmpty();}
- public boolean contains(Object o) {return c.contains(o);}
- public Object[] toArray() {return c.toArray();}
- public <T> T[] toArray(T[] a) {return c.toArray(a);}
- public String toString() {return c.toString();}
+ public int size() {return c.size();}
+ public boolean isEmpty() {return c.isEmpty();}
+ public boolean contains(Object o) {return c.contains(o);}
+ public Object[] toArray() {return c.toArray();}
+ public <T> T[] toArray(T[] a) {return c.toArray(a);}
+ public <T> T[] toArray(IntFunction<T[]> f) {return c.toArray(f);}
+ public String toString() {return c.toString();}
public Iterator<E> iterator() {
return new Iterator<E>() {
@@ -2020,6 +2022,9 @@
public <T> T[] toArray(T[] a) {
synchronized (mutex) {return c.toArray(a);}
}
+ public <T> T[] toArray(IntFunction<T[]> f) {
+ synchronized (mutex) {return c.toArray(f);}
+ }
public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
@@ -3049,14 +3054,15 @@
this.type = Objects.requireNonNull(type, "type");
}
- public int size() { return c.size(); }
- public boolean isEmpty() { return c.isEmpty(); }
- public boolean contains(Object o) { return c.contains(o); }
- public Object[] toArray() { return c.toArray(); }
- public <T> T[] toArray(T[] a) { return c.toArray(a); }
- public String toString() { return c.toString(); }
- public boolean remove(Object o) { return c.remove(o); }
- public void clear() { c.clear(); }
+ public int size() { return c.size(); }
+ public boolean isEmpty() { return c.isEmpty(); }
+ public boolean contains(Object o) { return c.contains(o); }
+ public Object[] toArray() { return c.toArray(); }
+ public <T> T[] toArray(T[] a) { return c.toArray(a); }
+ public <T> T[] toArray(IntFunction<T[]> f) { return c.toArray(f); }
+ public String toString() { return c.toString(); }
+ public boolean remove(Object o) { return c.remove(o); }
+ public void clear() { c.clear(); }
public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
@@ -5559,25 +5565,26 @@
implements Queue<E>, Serializable {
private static final long serialVersionUID = 1802017725587941708L;
private final Deque<E> q;
- AsLIFOQueue(Deque<E> q) { this.q = q; }
- public boolean add(E e) { q.addFirst(e); return true; }
- public boolean offer(E e) { return q.offerFirst(e); }
- public E poll() { return q.pollFirst(); }
- public E remove() { return q.removeFirst(); }
- public E peek() { return q.peekFirst(); }
- public E element() { return q.getFirst(); }
- public void clear() { q.clear(); }
- public int size() { return q.size(); }
- public boolean isEmpty() { return q.isEmpty(); }
- public boolean contains(Object o) { return q.contains(o); }
- public boolean remove(Object o) { return q.remove(o); }
- public Iterator<E> iterator() { return q.iterator(); }
- public Object[] toArray() { return q.toArray(); }
- public <T> T[] toArray(T[] a) { return q.toArray(a); }
- public String toString() { return q.toString(); }
- public boolean containsAll(Collection<?> c) {return q.containsAll(c);}
- public boolean removeAll(Collection<?> c) {return q.removeAll(c);}
- public boolean retainAll(Collection<?> c) {return q.retainAll(c);}
+ AsLIFOQueue(Deque<E> q) { this.q = q; }
+ public boolean add(E e) { q.addFirst(e); return true; }
+ public boolean offer(E e) { return q.offerFirst(e); }
+ public E poll() { return q.pollFirst(); }
+ public E remove() { return q.removeFirst(); }
+ public E peek() { return q.peekFirst(); }
+ public E element() { return q.getFirst(); }
+ public void clear() { q.clear(); }
+ public int size() { return q.size(); }
+ public boolean isEmpty() { return q.isEmpty(); }
+ public boolean contains(Object o) { return q.contains(o); }
+ public boolean remove(Object o) { return q.remove(o); }
+ public Iterator<E> iterator() { return q.iterator(); }
+ public Object[] toArray() { return q.toArray(); }
+ public <T> T[] toArray(T[] a) { return q.toArray(a); }
+ public <T> T[] toArray(IntFunction<T[]> f) { return q.toArray(f); }
+ public String toString() { return q.toString(); }
+ public boolean containsAll(Collection<?> c) { return q.containsAll(c); }
+ public boolean removeAll(Collection<?> c) { return q.removeAll(c); }
+ public boolean retainAll(Collection<?> c) { return q.retainAll(c); }
// We use inherited addAll; forwarding addAll would be wrong
// Override default methods in Collection
--- a/test/jdk/java/util/Collection/MOAT.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/Collection/MOAT.java Thu Jun 21 08:25:03 2018 -0700
@@ -415,6 +415,7 @@
equal(c.toString(),"[]");
equal(c.toArray().length, 0);
equal(c.toArray(new Object[0]).length, 0);
+ equal(c.toArray(Object[]::new).length, 0);
check(c.toArray(new Object[]{42})[0] == null);
Object[] a = new Object[1]; a[0] = Boolean.TRUE;
@@ -690,6 +691,13 @@
check(a.getClass() == Integer[].class);
}
+ {
+ Integer[] a = c.toArray(Integer[]::new);
+ equal(c.size(), a.length);
+ check(a.getClass() == Integer[].class);
+ check(Arrays.equals(c.toArray(new Integer[0]), a));
+ }
+
check(c.equals(c));
if (c instanceof Serializable) {
//System.out.printf("Serializing %s%n", c.getClass().getName());
--- a/test/jdk/java/util/concurrent/tck/ArrayDequeTest.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/ArrayDequeTest.java Thu Jun 21 08:25:03 2018 -0700
@@ -772,7 +772,7 @@
ArrayDeque l = new ArrayDeque();
l.add(new Object());
try {
- l.toArray(null);
+ l.toArray((Object[])null);
shouldThrow();
} catch (NullPointerException success) {}
}
--- a/test/jdk/java/util/concurrent/tck/BlockingQueueTest.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/BlockingQueueTest.java Thu Jun 21 08:25:03 2018 -0700
@@ -161,7 +161,7 @@
public void testToArray_NullArray() {
final Collection q = emptyCollection();
try {
- q.toArray(null);
+ q.toArray((Object[])null);
shouldThrow();
} catch (NullPointerException success) {}
}
--- a/test/jdk/java/util/concurrent/tck/Collection8Test.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/Collection8Test.java Thu Jun 21 08:25:03 2018 -0700
@@ -209,7 +209,7 @@
() -> c.iterator().forEachRemaining(null),
() -> c.spliterator().forEachRemaining(null),
() -> c.spliterator().tryAdvance(null),
- () -> c.toArray(null));
+ () -> c.toArray((Object[])null));
if (!impl.permitsNulls()) {
assertThrows(
--- a/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java Thu Jun 21 08:25:03 2018 -0700
@@ -712,7 +712,7 @@
public void testToArray_NullArg() {
ConcurrentLinkedDeque q = populatedDeque(SIZE);
try {
- q.toArray(null);
+ q.toArray((Object[])null);
shouldThrow();
} catch (NullPointerException success) {}
}
--- a/test/jdk/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java Thu Jun 21 08:25:03 2018 -0700
@@ -439,7 +439,7 @@
public void testToArray_NullArg() {
ConcurrentLinkedQueue q = populatedQueue(SIZE);
try {
- q.toArray(null);
+ q.toArray((Object[])null);
shouldThrow();
} catch (NullPointerException success) {}
}
--- a/test/jdk/java/util/concurrent/tck/LinkedListTest.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/LinkedListTest.java Thu Jun 21 08:25:03 2018 -0700
@@ -409,7 +409,7 @@
LinkedList l = new LinkedList();
l.add(new Object());
try {
- l.toArray(null);
+ l.toArray((Object[])null);
shouldThrow();
} catch (NullPointerException success) {}
}
--- a/test/jdk/java/util/concurrent/tck/SynchronousQueueTest.java Thu Jun 21 11:10:44 2018 -0400
+++ b/test/jdk/java/util/concurrent/tck/SynchronousQueueTest.java Thu Jun 21 08:25:03 2018 -0700
@@ -464,7 +464,7 @@
public void testToArray_null(boolean fair) {
final SynchronousQueue q = new SynchronousQueue(fair);
try {
- Object[] o = q.toArray(null);
+ Object[] o = q.toArray((Object[])null);
shouldThrow();
} catch (NullPointerException success) {}
}