Collections.emptyList().spliterator() is not ORDERED

Tagir F. Valeev amaembo at gmail.com
Sun Oct 25 16:49:26 UTC 2015


Hello!

>> PS> In this case we should fix Stream.concat to check if a
>> PS> spliterator reporting SIZED is empty, which will allow us to optimize the concatenation [1].
>> PS> [1] https://bugs.openjdk.java.net/browse/JDK-8022805

I tried to fix this issue, but stuck into subtle behavior change. My
idea is quite straightforward:

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
    Objects.requireNonNull(a);
    Objects.requireNonNull(b);

    @SuppressWarnings("unchecked")
    Spliterator<T> asplit = (Spliterator<T>) a.spliterator(); 
    @SuppressWarnings("unchecked")
    Spliterator<T> bsplit = (Spliterator<T>) b.spliterator(); 
    Spliterator<T> split;
    if (asplit.getExactSizeIfKnown() == 0)
        split = bsplit;
    else if (bsplit.getExactSizeIfKnown() == 0)
        split = asplit;
    else
        split = new Streams.ConcatSpliterator.OfRef<>(asplit, bsplit);
    Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
    return stream.onClose(Streams.composedClose(a, b));
}

Similar for primitive streams. The difference occurs when the
fail-fast source is modified after concat operation:

List<Integer> l1 = Arrays.asList(1,2,3);
List<Integer> l2 = new ArrayList<>();
Stream<Integer> s = Stream.concat(l1.stream(), l2.stream());
l2.add(4);
s.forEach(System.out::println);

In current implementation concat binds the late-binding spliterators
(by the way it's not documented and not very obvious that concat
binds; probably documentation update should be considered). Thus
currently this code produces

1
2
3
Exception in thread "main" java.util.ConcurrentModificationException
...

However after my change the exception disappears as the l2 spliterator
is not traversed at all. So do you think such behavior change is
acceptable or not?

Also some tests in tests/java/util/stream/ConcatTest.java which check
whether the resulting spliterator is ordered should be updated as now
concat may return ORDERED stream if one of the parties is UNORDERED,
but SIZED and empty. Next, I'm not absolutely sure whether the case
when both input streams are SIZED/empty should be handled separately
(return stream on Spliterators.emptySpliterator()?) or current
implementation (returning stream on second spliterator) is ok. Finally
the documentation probably should mention about special handling of
SIZED/empty stream. On the other hand currently it says:

> The resulting stream is ordered if both of the input streams are
> ordered...

It does not say "if and only if", thus probably the update is
unnecessary here.

So what do you think about this fix in general? Is it reasonable for
me to continue working on it and prepare full-fledged patch? Is there
any chance that this patch will be sponsored?

With best regards,
Tagir Valeev.




More information about the core-libs-dev mailing list