10

Given the following as an example of data classes:

class Country {

    List<Region> regions = new ArrayList<>();

    List<Region> getRegions() {
        return regions;
    }

}

class Region {

    String getName() {
        return "some name";
    }

}

Presuming I would have a List of Countries

    List<Country> countries = new ArrayList<>();

And I wanted to Stream those to their Regions and their corresponding names I would like to do the following:

    countries.stream().flatMap(Country::getRegions).map(Region::getName)...

However that code does not compile since the return value of "getRegions" is a Collection (List) as opposed to a Stream, which the flatMap Method accepts. But since I know that any Collection can be streamed via its Collection.stream() Method that shouldn't be a problem. Still I am forced to write it as follows:

    countries.stream().flatMap(c -> c.getRegions().stream()).map(Region::getName)...

Which is (given a richer context) far less readable than the former.

Questions is, is there any reason, that I am missing out on, for this to be that bulky? I have plenty of examples in our framework at which I am forced to take that route, always leaving me with a sour taste. (Guess I just have to add Kotlin to our projects and extend the Stream class with a flatMap Method that takes a Collection :p or do I?)

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Lukas
  • 244
  • 2
  • 13
  • I also tried the following as it made sense in my head, obviously it did not compile: Country::getRegions::stream – Lukas Feb 09 '17 at 16:54
  • 2
    I agree that `flatMap` should accept a collection, but I'm not sure SO is going to be able to provide a definitive explanation, other than "the people that wrote it didn't write it that way". – khelwood Feb 09 '17 at 16:54
  • "no" could also be an answer :) – Lukas Feb 09 '17 at 16:55
  • Please, check if Collectors.groupingBy helps you. Some examples available at: https://www.mkyong.com/java8/java-8-collectors-groupingby-and-mapping-example/ – Raffael Bechara Rameh Feb 09 '17 at 16:56
  • 3
    You could return a `Stream` from the method instead. Or add a method `regions` that does that. – Jorn Vernee Feb 09 '17 at 16:57
  • 3
    You can still do `.map(Country::getRegions).flatMap(List::stream)`, but I don’t get why method references are considered so superior to a simple lambda expression… – Holger Feb 09 '17 at 18:04

1 Answers1

17

A technical reason, which is not ideal but could be why this wasn't done. You can't overload on a generic type in Java.

They need to support

 Stream.flatMap(Function<Object, Stream<X>> function)

which means they can't overload it with

 Stream.flatMap(Function<Object, Collection<X>> function)

as these two methods have the same signature after erasure.

They could add a method

 Stream.flatMapCollection(Function<Object, Collection<X>> function)

or

 Stream.flatMapIterable(Function<Object, Iterable<X>> function)

 Stream.flatMapI(Function<Object, Iterable<X>> function)

but it wouldn't be pretty.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130