RFR: 8372946 - TreeMap sub-map entry spliterator is expensive [v4]

Oli Gillespie ogillespie at openjdk.org
Mon Feb 23 14:43:42 UTC 2026


On Mon, 23 Feb 2026 14:17:53 GMT, Oli Gillespie <ogillespie at openjdk.org> wrote:

>> test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/CollectionAndMapModifyStreamTest.java line 123:
>> 
>>> 121: 
>>> 122:         // The following are not lazy
>>> 123: //        maps.put(TreeMap.class.getName() + ".descendingMap()", () -> new TreeMap<>(content).descendingMap());
>> 
>> Is this test modification still necessary?
>
> That case still fails, yes, but I'm not totally sure why. I'm looking into it.
> 
> 
> java.util.ConcurrentModificationException
> 	at java.base/java.util.TreeMap$NavigableSubMap$SubMapIterator.prevEntry(TreeMap.java:2070)
> 	at java.base/java.util.TreeMap$NavigableSubMap$DescendingSubMapEntryIterator.next(TreeMap.java:2121)
> 	at java.base/java.util.TreeMap$NavigableSubMap$DescendingSubMapEntryIterator.next(TreeMap.java:2114)
> 	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
> 	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
> 	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
> 	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
> 	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:635)
> 	at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:291)
> 	at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:652)
> 	at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:658)
> 	at org.openjdk.tests.java.util.stream.CollectionAndMapModifyStreamTest.testEntrySetSizeRemove(CollectionAndMapModifyStreamTest.java:164)
> 	at org.openjdk.tests.java.util.stream.CollectionAndMapModifyStreamTest.testMapEntriesSizeRemove(CollectionAndMapModifyStreamTest.java:155)

Oh I understand now. The default Set spliterator is `Spliterator<T> spliterator(Collection<? extends T> c, int characteristics)`. It doesn't create an iterator until forEachRemaining is called, which in the test is *after* the .remove modification, so it doesn't observe a discrepancy. The new implementation uses creates the iterator up-front to pass to `spliteratorUnknownSize`, so in that case the iterator is created before the modification, hence CME.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/28608#discussion_r2841224287


More information about the core-libs-dev mailing list