8060192: Add default method <A> A[] Collection.toArray(IntFunction<A[]> generator)
authorsmarks
Thu, 21 Jun 2018 08:25:03 -0700
changeset 50697 3ef0862bbb3d
parent 50696 5c886cfc6ef5
child 50698 c1f7ece09b84
8060192: Add default method <A> A[] Collection.toArray(IntFunction<A[]> generator) Reviewed-by: martin, forax, psandoz, briangoetz
src/java.base/share/classes/java/util/Collection.java
src/java.base/share/classes/java/util/Collections.java
test/jdk/java/util/Collection/MOAT.java
test/jdk/java/util/concurrent/tck/ArrayDequeTest.java
test/jdk/java/util/concurrent/tck/BlockingQueueTest.java
test/jdk/java/util/concurrent/tck/Collection8Test.java
test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java
test/jdk/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java
test/jdk/java/util/concurrent/tck/LinkedListTest.java
test/jdk/java/util/concurrent/tck/SynchronousQueueTest.java
--- 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) {}
     }