Skip to content

Commit 41dbc13

Browse files
author
Stuart Marks
committed
8180352: Add Stream.toList() method
Reviewed-by: psandoz
1 parent 8969069 commit 41dbc13

File tree

8 files changed

+403
-87
lines changed

8 files changed

+403
-87
lines changed

src/java.base/share/classes/java/util/ImmutableCollections.java

Lines changed: 208 additions & 71 deletions
Large diffs are not rendered by default.

src/java.base/share/classes/java/util/List.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ static <E> List<E> of(E e1, E e2) {
842842
* @since 9
843843
*/
844844
static <E> List<E> of(E e1, E e2, E e3) {
845-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3);
845+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3);
846846
}
847847

848848
/**
@@ -861,7 +861,7 @@ static <E> List<E> of(E e1, E e2, E e3) {
861861
* @since 9
862862
*/
863863
static <E> List<E> of(E e1, E e2, E e3, E e4) {
864-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4);
864+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4);
865865
}
866866

867867
/**
@@ -881,7 +881,7 @@ static <E> List<E> of(E e1, E e2, E e3, E e4) {
881881
* @since 9
882882
*/
883883
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
884-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5);
884+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5);
885885
}
886886

887887
/**
@@ -902,8 +902,8 @@ static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
902902
* @since 9
903903
*/
904904
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
905-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
906-
e6);
905+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
906+
e6);
907907
}
908908

909909
/**
@@ -925,8 +925,8 @@ static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
925925
* @since 9
926926
*/
927927
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
928-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
929-
e6, e7);
928+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
929+
e6, e7);
930930
}
931931

932932
/**
@@ -949,8 +949,8 @@ static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
949949
* @since 9
950950
*/
951951
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
952-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
953-
e6, e7, e8);
952+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
953+
e6, e7, e8);
954954
}
955955

956956
/**
@@ -974,8 +974,8 @@ static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
974974
* @since 9
975975
*/
976976
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
977-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
978-
e6, e7, e8, e9);
977+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
978+
e6, e7, e8, e9);
979979
}
980980

981981
/**
@@ -1000,8 +1000,8 @@ static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
10001000
* @since 9
10011001
*/
10021002
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) {
1003-
return ImmutableCollections.ListN.fromTrustedArray(e1, e2, e3, e4, e5,
1004-
e6, e7, e8, e9, e10);
1003+
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
1004+
e6, e7, e8, e9, e10);
10051005
}
10061006

10071007
/**
@@ -1042,7 +1042,7 @@ static <E> List<E> of(E... elements) {
10421042
case 2:
10431043
return new ImmutableCollections.List12<>(elements[0], elements[1]);
10441044
default:
1045-
return ImmutableCollections.ListN.fromArray(elements);
1045+
return ImmutableCollections.listFromArray(elements);
10461046
}
10471047
}
10481048

src/java.base/share/classes/java/util/stream/ReferencePipeline.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.util.Comparator;
2828
import java.util.Iterator;
29+
import java.util.List;
2930
import java.util.Objects;
3031
import java.util.Optional;
3132
import java.util.Spliterator;
@@ -44,6 +45,7 @@
4445
import java.util.function.ToDoubleFunction;
4546
import java.util.function.ToIntFunction;
4647
import java.util.function.ToLongFunction;
48+
import jdk.internal.access.SharedSecrets;
4749

4850
/**
4951
* Abstract base class for an intermediate pipeline stage or pipeline source
@@ -620,6 +622,11 @@ public final Object[] toArray() {
620622
return toArray(Object[]::new);
621623
}
622624

625+
@Override
626+
public List<P_OUT> toList() {
627+
return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArrayNullsAllowed(this.toArray());
628+
}
629+
623630
@Override
624631
public final boolean anyMatch(Predicate<? super P_OUT> predicate) {
625632
return evaluate(MatchOps.makeRef(predicate, MatchOps.MatchKind.ANY));

src/java.base/share/classes/java/util/stream/Stream.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,40 @@ <R> R collect(Supplier<R> supplier,
11621162
*/
11631163
<R, A> R collect(Collector<? super T, A, R> collector);
11641164

1165+
/**
1166+
* Accumulates the elements of this stream into a {@code List}. The elements in
1167+
* the list will be in this stream's encounter order, if one exists. The returned List
1168+
* is unmodifiable; calls to any mutator method will always cause
1169+
* {@code UnsupportedOperationException} to be thrown. There are no
1170+
* guarantees on the implementation type or serializability of the returned List.
1171+
*
1172+
* <p>The returned instance may be <a href="../lang/doc-files/ValueBased.html">value-based</a>.
1173+
* Callers should make no assumptions about the identity of the returned instances.
1174+
* Identity-sensitive operations on these instances (reference equality ({@code ==}),
1175+
* identity hash code, and synchronization) are unreliable and should be avoided.
1176+
*
1177+
* <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
1178+
*
1179+
* @apiNote If more control over the returned object is required, use
1180+
* {@link Collectors#toCollection(Supplier)}.
1181+
*
1182+
* @implSpec The implementation in this interface returns a List produced as if by the following:
1183+
* <pre>{@code
1184+
* Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
1185+
* }</pre>
1186+
*
1187+
* @implNote Most instances of Stream will override this method and provide an implementation
1188+
* that is highly optimized compared to the implementation in this interface.
1189+
*
1190+
* @return a List containing the stream elements
1191+
*
1192+
* @since 16
1193+
*/
1194+
@SuppressWarnings("unchecked")
1195+
default List<T> toList() {
1196+
return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
1197+
}
1198+
11651199
/**
11661200
* Returns the minimum element of this stream according to the provided
11671201
* {@code Comparator}. This is a special case of a

src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@
2929

3030
public interface JavaUtilCollectionAccess {
3131
<E> List<E> listFromTrustedArray(Object[] array);
32+
<E> List<E> listFromTrustedArrayNullsAllowed(Object[] array);
3233
}

test/jdk/java/util/Collection/MOAT.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -229,7 +229,16 @@ public static void realMain(String[] args) {
229229
List.of(1, 2, 3, 4, 5, 6, 7, 8),
230230
List.of(1, 2, 3, 4, 5, 6, 7, 8, 9),
231231
List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
232-
List.of(integerArray))) {
232+
List.of(integerArray),
233+
Stream.<Integer>empty().toList(),
234+
Stream.of(1).toList(),
235+
Stream.of(1, 2).toList(),
236+
Stream.of(1, 2, 3).toList(),
237+
Stream.of(1, 2, 3, 4).toList(),
238+
Stream.of((Integer)null).toList(),
239+
Stream.of(1, null).toList(),
240+
Stream.of(1, null, 3).toList(),
241+
Stream.of(1, null, 3, 4).toList())) {
233242
testCollection(list);
234243
testImmutableList(list);
235244
testListMutatorsAlwaysThrow(list);
@@ -1096,6 +1105,15 @@ private static void testList(final List<Integer> l) {
10961105
catch (UnsupportedOperationException ignored) {/* OK */}
10971106
catch (Throwable t) { unexpected(t); }
10981107
}
1108+
1109+
int hashCode = 1;
1110+
for (Integer i : l)
1111+
hashCode = 31 * hashCode + (i == null ? 0 : i.hashCode());
1112+
check(l.hashCode() == hashCode);
1113+
1114+
var t = new ArrayList<>(l);
1115+
check(t.equals(l));
1116+
check(l.equals(t));
10991117
}
11001118

11011119
private static void testCollection(Collection<Integer> c) {
@@ -1131,6 +1149,13 @@ private static void testCollection1(Collection<Integer> c) {
11311149
if (c instanceof List)
11321150
testList((List<Integer>)c);
11331151

1152+
if (c instanceof Set) {
1153+
int hashCode = 0;
1154+
for (Integer i : c)
1155+
hashCode = hashCode + (i == null ? 0 : i.hashCode());
1156+
check(c.hashCode() == hashCode);
1157+
}
1158+
11341159
check(supportsRemove(c));
11351160

11361161
try {
@@ -1230,6 +1255,15 @@ private static void checkFunctionalInvariants(Map<Integer,Integer> m) {
12301255
private static void testMap(Map<Integer,Integer> m) {
12311256
System.out.println("\n==> " + m.getClass().getName());
12321257

1258+
int hashCode = 0;
1259+
for (var e : m.entrySet()) {
1260+
int entryHash = (e.getKey() == null ? 0 : e.getKey().hashCode()) ^
1261+
(e.getValue() == null ? 0 : e.getValue().hashCode());
1262+
check(e.hashCode() == entryHash);
1263+
hashCode += entryHash;
1264+
}
1265+
check(m.hashCode() == hashCode);
1266+
12331267
if (m instanceof ConcurrentMap)
12341268
testConcurrentMap((ConcurrentMap<Integer,Integer>) m);
12351269

test/jdk/java/util/List/ListFactories.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.Iterator;
3434
import java.util.List;
3535
import java.util.ListIterator;
36+
import java.util.stream.Stream;
3637

3738
import org.testng.annotations.DataProvider;
3839
import org.testng.annotations.Test;
@@ -359,6 +360,19 @@ public void copyOfRejectsNullElements() {
359360
List<Integer> list = List.copyOf(Arrays.asList(1, null, 3));
360361
}
361362

363+
@Test(expectedExceptions=NullPointerException.class)
364+
public void copyOfRejectsNullElements2() {
365+
List<String> list = List.copyOf(Stream.of("a", null, "c").toList());
366+
}
367+
368+
@Test
369+
public void copyOfCopiesNullAllowingList() {
370+
List<String> orig = Stream.of("a", "b", "c").toList();
371+
List<String> copy = List.copyOf(orig);
372+
373+
assertNotSame(orig, copy);
374+
}
375+
362376
@Test
363377
public void iteratorShouldNotBeListIterator() {
364378
List<Integer> list = List.of(1, 2, 3, 4, 5);
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package org.openjdk.tests.java.util.stream;
24+
25+
import org.testng.annotations.Test;
26+
27+
import java.util.*;
28+
import java.util.stream.*;
29+
30+
import static java.util.stream.LambdaTestHelpers.*;
31+
import static org.testng.Assert.assertEquals;
32+
import static org.testng.Assert.assertFalse;
33+
import static org.testng.Assert.assertTrue;
34+
35+
36+
/**
37+
* ToListOpTest
38+
*/
39+
@Test
40+
public class ToListOpTest extends OpTestCase {
41+
42+
public void testToList() {
43+
assertCountSum(countTo(0).stream().toList(), 0, 0);
44+
assertCountSum(countTo(10).stream().toList(), 10, 55);
45+
}
46+
47+
private void checkUnmodifiable(List<Integer> list) {
48+
try {
49+
list.add(Integer.MIN_VALUE);
50+
fail("List.add did not throw UnsupportedOperationException");
51+
} catch (UnsupportedOperationException ignore) { }
52+
53+
if (list.size() > 0) {
54+
try {
55+
list.set(0, Integer.MAX_VALUE);
56+
fail("List.set did not throw UnsupportedOperationException");
57+
} catch (UnsupportedOperationException ignore) { }
58+
}
59+
}
60+
61+
@Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
62+
public void testOps(String name, TestData.OfRef<Integer> data) {
63+
List<Integer> objects = exerciseTerminalOps(data, s -> s.toList());
64+
checkUnmodifiable(objects);
65+
assertFalse(objects.contains(null));
66+
}
67+
68+
@Test(dataProvider = "withNull:StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
69+
public void testOpsWithNull(String name, TestData.OfRef<Integer> data) {
70+
List<Integer> objects = exerciseTerminalOps(data, s -> s.toList());
71+
checkUnmodifiable(objects);
72+
assertTrue(objects.contains(null));
73+
}
74+
75+
@Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
76+
public void testDefaultOps(String name, TestData.OfRef<Integer> data) {
77+
List<Integer> objects = exerciseTerminalOps(data, s -> DefaultMethodStreams.delegateTo(s).toList());
78+
checkUnmodifiable(objects);
79+
assertFalse(objects.contains(null));
80+
}
81+
82+
@Test(dataProvider = "withNull:StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
83+
public void testDefaultOpsWithNull(String name, TestData.OfRef<Integer> data) {
84+
List<Integer> objects = exerciseTerminalOps(data, s -> DefaultMethodStreams.delegateTo(s).toList());
85+
checkUnmodifiable(objects);
86+
assertTrue(objects.contains(null));
87+
}
88+
89+
}

0 commit comments

Comments
 (0)