Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the spliterator from a sorted stream throws an exception

According to the doc of Spliterator#getComparator, it states

If this Spliterator's source is SORTED by a Comparator, returns that Comparator. If the source is SORTED in natural order, returns null. Otherwise, if the source is not SORTED, throws IllegalStateException.

Implementation Requirements:

The default implementation always throws IllegalStateException.

Returns: a Comparator, or null if the elements are sorted in the natural order.

Throws: IllegalStateException - if the spliterator does not report a characteristic of SORTED.

So when running this piece of code

Spliterator<Integer> spliterator = Stream.of(1, 2, 3).sorted().spliterator();

System.out.println((spliterator.characteristics() & Spliterator.SORTED) == Spliterator.SORTED);
System.out.println(spliterator.getComparator());

I get:

true
null

So far so good. Now when doing this:

Spliterator<Integer> spliterator = Stream.of(1, 2, 3).sorted(Comparator.naturalOrder()).spliterator();

System.out.println((spliterator.characteristics() & Spliterator.SORTED) == Spliterator.SORTED);
System.out.println(spliterator.getComparator());

It outputs false and throws an exception:

Exception in thread "main" java.lang.IllegalStateException
    at java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.getComparator(StreamSpliterators.java:259)
    at SpliteratorTest.main(SpliteratorTest.java:10)

Why does it outputs false and throws an exception?

Shouldn't it give me the Comparator I supplied to sorted(), according to the documentation?

(This happens also with reverseOrder(), or comparing(identity()), etc.).

like image 344
user2336315 Avatar asked Jul 01 '15 10:07

user2336315


1 Answers

Internally streams are using StreamOpFlag enum which is somewhat different from spliterator flags. Flags are converted using the java.util.stream.StreamOpFlag.fromCharacteristics(Spliterator<?>) method which implementation is the following:

static int fromCharacteristics(Spliterator<?> spliterator) {
    int characteristics = spliterator.characteristics();
    if ((characteristics & Spliterator.SORTED) != 0 && spliterator.getComparator() != null) {
        // Do not propagate the SORTED characteristic if it does not correspond
        // to a natural sort order
        return characteristics & SPLITERATOR_CHARACTERISTICS_MASK & ~Spliterator.SORTED;
    }
    else {
        return characteristics & SPLITERATOR_CHARACTERISTICS_MASK;
    }
}

Seems that internally the SORTED characteristic for the spliterator which is not explicitly natural ordered is unnecessary for the stream API, so it's not preserved. Actually it's never specified in the documentation that sorted(comparator).spliterator() must return the spliterator with SORTED characteristic. The spliterator documentation says that if it has SORTED characteristic, it must return the comparator, but there's no case when having the SORTED characteristic is required, thus it's up to implementation. This can change in future, but it's not a bug.

Update: just noticed that in JDK-9 there's a clear statement added into the documentation of spliterator() method:

The returned spliterator should report the set of characteristics derived from the stream pipeline (namely the characteristics derived from the stream source spliterator and the intermediate operations). Implementations may report a sub-set of those characteristics. For example, it may be too expensive to compute the entire set for some or all possible stream pipelines.

See the JDK-8048689 bug report.

like image 120
Tagir Valeev Avatar answered Nov 12 '22 01:11

Tagir Valeev