Reputation: 330
When working with a Stream<Collection<T>>
, I would like to apply the usual methods from the Java 8 Stream API to every element of type T in every collection in my stream. As the title states, I mainly want to map the elements, but of course all the other methods (e.g. filter(Predicate)
or allMatch(Predicate)
) are of interest as well.
Consider the example of a 2D board storing multiple rectangle tiles. Each tile is defined by the (x, y)-positions it occupies on the board.
List<Position> tile1 = Lists.asList(tile1Pos1, tile1Pos2, ...)
List<Position> tile2 = Lists.asList(tile2Pos1, tile2Pos2, ...)
...
List<List<Position>> board = Lists.asList(tile1, tile2, ...)
When rotating the board, I want to convert all the positions of the tiles. I need to apply the same x to y and y to x mapping function to every position. However, I have to retain the association between the tiles and their respective positions.
Currently, I am doing the following with my Stream<List<Position>> streamOfCollections
:
List<List<Position>> rotatedBoard = board.stream().map(tile -> tile.stream().map(Position::mapperFunctionRotate).collect(toList())).collect(toList());
Note that I am retaining the original layout of my data. Each position is still associated to the one specific tile it is a part of.
Generalizing, for any Stream<Collection<T>>
I want to modify all the individual elements of type T while keeping them in their collection.
Ideally, I would like to be able to do board.stream().mapIndividually(Position::mapperFunctionRotate)
which has a similar method signature as flatMap
in the Java Stream API, but does not flatten my stream of lists.
Are there any convenient ways to simplify the above mapping function?
I am also looking for libraries or frameworks which get the job done. As it seems to me that the Java API does not support such a thing, other libraries are actually the main scope of this question, I think.
I already looked at guava which offers data structures like Multimap<K, V>
behaving like a Map<K, Collection<V>>
, but I had no luck finding extensions for/of the Java Stream API.
Upvotes: 2
Views: 369
Reputation: 298123
It’s not clear what you are aiming at. The solution you have shown is already the canonical solution. If you want to do similar things repeatedly, you may abstract it into a method:
public static <T,R> List<List<R>> applyToAll(
Collection<? extends Collection<? extends T>> source,
Function<? super T, ? extends R> op) {
return source.stream()
.map(c -> c.stream().map(op).collect(Collectors.toList()))
.collect(Collectors.toList());
}
which you can use in your specific case as
List<List<Position>> rotatedBoard = applyToAll(board, Position::mapperFunctionRotate);
Generally, there is no shortcut for “convert every element of a collection and produce a collection”, hence, there is also no shortcut for the same thing in a nested context.
Of course, you could create your own and implement the nested variant atop:
public static <T,R> List<R> mapAll(
Collection<T> source, Function<? super T, ? extends R> op) {
return source.stream().map(op).collect(Collectors.toList());
}
…
List<List<Position>> rotatedBoard
= mapAll(board, l -> mapAll(l, Position::mapperFunctionRotate));
If you think, it doesn’t feel FP enough, you could declare it as
public static <T,R,C extends Collection<T>>
Function<C, List<R>> mapAll(Function<? super T, ? extends R> op) {
return source -> source.stream().map(op).collect(Collectors.toList());
}
to be used like
Function<List<List<Position>>,List<List<Position>>> f
= mapAll(mapAll(Position::mapperFunctionRotate));
List<List<Position>> rotatedBoard = f.apply(board);
unfortunately, Java requires an explicit declaration of the function type here, otherwise, mapAll(mapAll(Position::mapperFunctionRotate)).apply(board)
would make a great use case, especially as it would be easy to extend to more dimensions. But Java’s (current) type inference can’t deal with that.
You can, however, combine both mapAll
methods above to
List<List<Position>> rotatedBoard = mapAll(board, mapAll(Position::mapperFunctionRotate));
Upvotes: 2
Reputation: 120848
Aren't you looking for flatmap here?
.stream().flatMap(Collection::stream).collect(Collectors.toList());
Upvotes: 3