Skip to content

Commit 1c2a553

Browse files
committed
8327858: Improve spliterator and forEach for single-element immutable collections
Reviewed-by: smarks, vklang
1 parent a449aee commit 1c2a553

File tree

3 files changed

+126
-2
lines changed

3 files changed

+126
-2
lines changed

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import java.io.ObjectStreamException;
3333
import java.io.Serializable;
3434
import java.lang.reflect.Array;
35+
import java.util.function.BiConsumer;
3536
import java.util.function.BiFunction;
37+
import java.util.function.Consumer;
3638
import java.util.function.Function;
3739
import java.util.function.Predicate;
3840
import java.util.function.UnaryOperator;
@@ -656,6 +658,24 @@ public <T> T[] toArray(T[] a) {
656658
}
657659
return array;
658660
}
661+
662+
@Override
663+
@SuppressWarnings("unchecked")
664+
public void forEach(Consumer<? super E> action) {
665+
action.accept(e0); // implicit null check
666+
if (e1 != EMPTY) {
667+
action.accept((E) e1);
668+
}
669+
}
670+
671+
@Override
672+
public Spliterator<E> spliterator() {
673+
if (e1 == EMPTY) {
674+
return Collections.singletonSpliterator(e0);
675+
} else {
676+
return super.spliterator();
677+
}
678+
}
659679
}
660680

661681
@jdk.internal.ValueBased
@@ -895,6 +915,26 @@ public <T> T[] toArray(T[] a) {
895915
}
896916
return array;
897917
}
918+
919+
@Override
920+
@SuppressWarnings("unchecked")
921+
public void forEach(Consumer<? super E> action) {
922+
if (e1 == EMPTY) {
923+
action.accept(e0); // implicit null check
924+
} else {
925+
action.accept(REVERSE ? (E)e1 : e0); // implicit null check
926+
action.accept(REVERSE ? e0 : (E)e1);
927+
}
928+
}
929+
930+
@Override
931+
public Spliterator<E> spliterator() {
932+
if (e1 == EMPTY) {
933+
return Collections.singletonSpliterator(e0);
934+
} else {
935+
return super.spliterator();
936+
}
937+
}
898938
}
899939

900940

@@ -1158,6 +1198,11 @@ private Object writeReplace() {
11581198
public int hashCode() {
11591199
return k0.hashCode() ^ v0.hashCode();
11601200
}
1201+
1202+
@Override
1203+
public void forEach(BiConsumer<? super K, ? super V> action) {
1204+
action.accept(k0, v0); // implicit null check
1205+
}
11611206
}
11621207

11631208
/**

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* @bug 6207984 6272521 6192552 6269713 6197726 6260652 5073546 4137464
2727
* 4155650 4216399 4294891 6282555 6318622 6355327 6383475 6420753
2828
* 6431845 4802633 6570566 6570575 6570631 6570924 6691185 6691215
29-
* 4802647 7123424 8024709 8193128
29+
* 4802647 7123424 8024709 8193128 8327858
3030
* @summary Run many tests on many Collection and Map implementations
3131
* @author Martin Buchholz
3232
* @modules java.base/java.util:open
@@ -479,6 +479,8 @@ private static <T> void testImmutableCollection(final Collection<T> c, T t) {
479479
() -> c.removeAll(singleton(first)),
480480
() -> c.retainAll(emptyList()));
481481
}
482+
testForEachMatch(c);
483+
testSpliteratorMatch(c);
482484
}
483485

484486
private static <T> void testImmutableSeqColl(final SequencedCollection<T> c, T t) {
@@ -540,6 +542,39 @@ private static void testCollMutatorsAlwaysThrow(Collection<Integer> c) {
540542
() -> c.retainAll(c));
541543
}
542544

545+
// Ensures forEach supplies in the same order as the iterator
546+
private static <T> void testForEachMatch(Collection<T> c) {
547+
var itr = c.iterator();
548+
int[] index = {0};
549+
c.forEach(item -> {
550+
T itrNext = null;
551+
if (!itr.hasNext() || !Objects.equals(itrNext = itr.next(), item)) {
552+
fail("forEach and iterator mismatch at " + index[0] + " forEach: " + item + ", itr: " + itrNext);
553+
}
554+
index[0]++;
555+
});
556+
if (itr.hasNext()) {
557+
fail("forEach and iterator mismatch at tail, extras in itr");
558+
}
559+
}
560+
561+
// Ensures spliterator returns in the same order as the iterator
562+
private static <T> void testSpliteratorMatch(Collection<T> c) {
563+
var itr = c.iterator();
564+
var split = c.spliterator();
565+
int[] index = {0};
566+
split.forEachRemaining(item -> {
567+
T itrNext = null;
568+
if (!itr.hasNext() || !Objects.equals(itrNext = itr.next(), item)) {
569+
fail("iterator and spliterator mismatch at " + index[0] + " spliterator: " + item + ", itr: " + itrNext);
570+
}
571+
index[0]++;
572+
});
573+
if (itr.hasNext()) {
574+
fail("iterator and spliterator mismatch at tail, extra item in itr");
575+
}
576+
}
577+
543578
/**
544579
* Test that calling a mutator always throws UOE, even if the mutator
545580
* wouldn't actually do anything on an empty collection.

test/micro/org/openjdk/bench/java/util/ImmutableColls.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2024, 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
@@ -297,6 +297,50 @@ public void iterateSet(Blackhole bh, Set<String> coll) {
297297
}
298298
}
299299

300+
@Benchmark
301+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
302+
public void forEachOverSet(Blackhole bh) {
303+
forEachSet(bh, fs4);
304+
forEachSet(bh, s1);
305+
forEachSet(bh, s3);
306+
forEachSet(bh, fs2);
307+
forEachSet(bh, s0);
308+
}
309+
310+
public void forEachSet(Blackhole bh, Set<String> coll) {
311+
coll.forEach(bh::consume);
312+
}
313+
314+
@Benchmark
315+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
316+
public void iterateOverList(Blackhole bh) {
317+
iterateList(bh, fl4);
318+
iterateList(bh, fl1);
319+
iterateList(bh, l3);
320+
iterateList(bh, l0);
321+
iterateList(bh, fl2);
322+
}
323+
324+
public void iterateList(Blackhole bh, List<String> coll) {
325+
for (String s : coll) {
326+
bh.consume(s);
327+
}
328+
}
329+
330+
@Benchmark
331+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
332+
public void forEachOverList(Blackhole bh) {
333+
forEachList(bh, fl4);
334+
forEachList(bh, fl1);
335+
forEachList(bh, l3);
336+
forEachList(bh, l0);
337+
forEachList(bh, fl2);
338+
}
339+
340+
public void forEachList(Blackhole bh, List<String> coll) {
341+
coll.forEach(bh::consume);
342+
}
343+
300344
@Benchmark
301345
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
302346
public void toArrayFromMap(Blackhole bh) {

0 commit comments

Comments
 (0)