19

The partitioningBy collector applies a predicate to each element in a stream and produces a map from booleans to lists of elements from the stream that satisfied or didn't satisfy the predicate. For instance:

Stream.of(1,2,3,4).collect(partitioningBy(x -> x >= 3))
// {false=[1, 2], true=[3, 4]}

As discussed in What's the purpose of partitioningBy, the observed behavior is that partitioningBy always returns a map with entries for both true and false. E.g.:

Stream.empty().collect(partitioningBy(x -> false));
// {false=[], true=[]}

Stream.of(1,2,3).collect(partitioningBy(x -> false));
// {false=[1, 2, 3], true=[]}

Stream.of(1,2,3).collect(partitioningBy(x -> true));
// {false=[], true=[1, 2, 3]}

Is that behavior actually specified somewhere? The Javadoc only says:

Returns a Collector which partitions the input elements according to a Predicate, and organizes them into a Map<Boolean, List<T>>. There are no guarantees on the type, mutability, serializability, or thread-safety of the Map returned.

Could a conforming implementation return these instead:

Stream.empty().collect(partitioningBy(x -> false));
// {}, or {false=[]}, or {true=[]}

Stream.of(1,2,3).collect(partitioningBy(x -> false));
// {false=[1, 2, 3]}

Stream.of(1,2,3).collect(partitioningBy(x -> true));
// {true=[1, 2, 3]}

The corresponding JSR 335 only seems to include the same documentation, but not additional discussion about what entries the map will contain.

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • 3
    In Java 8, presence of both entries isn't specified, but in any implementation based on OpenJDK, both entries will always be present. Hypothetically a clean-room JDK implementation might have different behavior, but this seems unlikely to me. As user140547's answer noted, the Java 9 specification has been modified to require that both entries always be present. – Stuart Marks Dec 22 '16 at 18:00
  • 2
    @StuartMarks That was part of what motivated my question. It seems like the quickest clean-room implementation based just on the Java 8 docs would be `partitioningBy(Predicate predicate) { return groupingBy(predicate::test); }`, which wouldn't provide the empty lists in the results. – Joshua Taylor Dec 23 '16 at 11:41
  • 1
    @JoshuaTaylor: True, but as there are only two possible and known keys (true and false), it would be annoying if you had to do a null check before iterating the lists... – user140547 Dec 23 '16 at 14:27
  • 1
    @user140547 That's exactly the point. The Java 8 docs don't provide the guarantee that both entries will be present, so (strictly speaking), you already have to do the null/containsKey checks. That's why I wanted to know if there's a guarantee that both keys will have values. The Java 9 docs provide that guarantee, so that the checks aren't necessary. – Joshua Taylor Dec 23 '16 at 14:29

1 Answers1

17

In Java 9 Javadoc of the method, there is a clarification which makes it more explicit:

The returned Map always contains mappings for both false and true keys.

user140547
  • 7,750
  • 3
  • 28
  • 80
  • 4
    Might be quiet fresh as we had [that discussion](http://stackoverflow.com/questions/41036522/combine-allmatch-nonematch-and-anymatch-on-a-single-stream/41036977#comment69298278_41036977) only recently… – Holger Dec 22 '16 at 17:32
  • 5
    @Holger Yep, I popped in the spec change right after that, and the build with the change (b150) was posted just today. – Stuart Marks Dec 22 '16 at 17:49