3

Which stream operations use CONCURRENT,IMMUTABLE and NONNULL Spliterator characteristics? How does each of them help in those operations?


I'm not asking what those flags are, this can be found easly in the documantation. I'm asking which operations use them and how.

Stav Alfi
  • 13,139
  • 23
  • 99
  • 171

3 Answers3

3

First, you should make a clear distinction that you are asking about Spliterator characteristics here and these depend on the source of the Stream; because there are also (CONCURRENT, UNORDERED and IDENTITY_FINISH for Collectors for example).

There is a comment in StreamOpFlag saying:

// The following Spliterator characteristics are not currently used but a
// gap in the bit set is deliberately retained to enable corresponding
// stream flags if//when required without modification to other flag values.
//
// 4, 0x00000100 NONNULL(4, ...
// 5, 0x00000400 IMMUTABLE(5, ...
// 6, 0x00001000 CONCURRENT(6, ...
// 7, 0x00004000 SUBSIZED(7, ...

As far as I understand these are not a direct 1 to 1 mapping with the ones from Spliterator, but still they are not used.

At the moment (and I've searched the jdk-8 and 9 sources), neither are leveraged - but still are reported by some implementations of Spliterators (Arrays report IMMUTABLE and ConcurrentHashMap reports NONNULL for example).

These flags could be used in the future on the other hand - if you know that a source can not contain null elements (NONNULL), obviously you can skip some null checks or define some state with a null. I can't think of any examples for CONCURRENT or IMMUTABLE, but there might be such.

For example under the current implementation for an UNORDERED and CONCURRENT collector (!= Spliterator properties), the combiner is not called when you do toConcurrentMap. For example:

Set.of("one", "two", "das", "dasda")
            .stream()
            .parallel()
            .collect(Collectors.toConcurrentMap(Function.identity(), String::length));

will not call the combiner - since there is no need to.

Optimizations like these could be made for any of the 3 characteristics that you have mentioned. For example you can read this where StreamOpFlag.ORDERED has changed the result of findFirst in java 8 vs java 9

Eugene
  • 117,005
  • 15
  • 201
  • 306
1

In Java 8, the stream operations use neither of these 3 characteristics. This can be checked by searching these constants in the Java sources.

However, the CONCURRENT characteristic may affect the behaviour of parallel streams when you write your own collection. If you create a Spliterator from a collection and not report the CONCURRENT characteristic, then the spliterator will additionally have SIZED and SUBSIZED characteristics:

Collection<Integer> col = ...
Spliterator<Integer> s = Spliterators.spliterator(col, 0);

System.out.println(s.hasCharacteristics(Spliterator.SIZED)); // Prints true
System.out.println(s.hasCharacteristics(Spliterator.SUBSIZED)); // Prints true

But if you report the CONCURRENT characteristic, then the spliterator is not SIZED anymore:

Collection<Integer> col = ...
Spliterator<Integer> s = Spliterators.spliterator(col, Spliterator.CONCURRENT);

System.out.println(s.hasCharacteristics(Spliterator.SIZED)); // Prints false
System.out.println(s.hasCharacteristics(Spliterator.SUBSIZED)); // Prints false

Spliterators that are not SIZED and SUBSIZED are poorly parallelized, so when you write you own concurrent collection, it is better to write a custom spliterator and not rely on the default spliterator implementation.

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
  • well... that's *specific* to `Spliterators.spliterator(Collection)` because they create the Spliterator from the `Iterator`; that's not a general rule. Usually collections implement their own Spliterators much better. – Eugene Aug 16 '17 at 12:25
  • @Eugene Yes, I noted this in my answer (last sentence) – ZhekaKozlov Aug 16 '17 at 15:54
1

People say that IMMUTABLE isn't used but as it turns out, Stream#iterate reports IMMUTABLE:

jshell> Stream.iterate(0, i -> i + 1).spliterator().characteristics()
$1 ==> 1040

1040 is IMMUTABLE | ORDERED but it looks like any operation drops IMMUTABLE (tested with map, filter, flatMap, limit, skip; tested on Java 16)
IntStream#iterate, LongStream#iterate and DoubleStream#iterate report NONNULL as well but that is also dropped by the next operation.

Loading BG
  • 131
  • 2
  • 9