7

I'm trying to use flatmap to make a nested loop with the Stream API, but I can't seem to figure it out. As an example, I want to recreate the following loop:

List<String> xs = Arrays.asList(new String[]{ "one","two", "three"});
List<String> ys = Arrays.asList(new String[]{"four", "five"});

System.out.println("*** Nested Loop ***");
for (String x : xs)
    for (String y : ys)
        System.out.println(x + " + " + y);

I can do it like this, but this seems ugly:

System.out.println("*** Nested Stream ***");
xs.stream().forEach(x ->
    ys.stream().forEach(y -> System.out.println(x + " + " + y))
);

Flatmap looks promising, but how can I access the variable in the outer loop?

System.out.println("*** Flatmap *** ");
xs.stream().flatMap(x -> ys.stream()).forEach(y -> System.out.println("? + " + y));

Output:

*** Nested Loop ***
one + four
one + five
two + four
two + five
three + four
three + five
*** Nested Stream ***
one + four
one + five
two + four
two + five
three + four
three + five
*** Flatmap *** 
? + four
? + five
? + four
? + five
? + four
? + five
1

3 Answers 3

12

You have to create your desired elements in the flatMap stage, like:

xs.stream().flatMap(x -> ys.stream().map(y -> x + " + " + y)).forEach(System.out::println);
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. It seems to me the nested Stream option would be easier to use though. In the given example you can quite easily move the combination operation to flatmap, but in a lot of other cases this will be more work, especially when adding more levels to the nesting.
@wvdz It always depends on the use case and what is more readable to you.
2

Normally, there is no need of the flatMap:

xs.forEach(x -> ys.stream().map(y ->  x + " + " + y).forEach(System.out::println)); // X
xs.forEach(x -> ys.forEach(y -> System.out.println(x + " + " + y))); // V

as well as there is no need of Stream API here.

Yes, it looks beautiful, but only with such childish tasks. You create/close a new stream for each element only to merge them into the resulting stream. And all of that is just for printing out?

In contrast, the forEach provides a one-line solution without any performance costs (a standard foreach inside).

1 Comment

This is equivalent to the nested stream example that I gave in the question.
0

Basically, this is a cartesian product for those lists. I would combine them first into one list:

List<String> xs = Arrays.asList(new String[]{ "one","two", "three"});
List<String> ys = Arrays.asList(new String[]{"four", "five"});
List<List<String>> input = Arrays.asList(xs, ys);

Then create a stream of lists and each of the lists will map to its own stream and hold this stuff into Supplier:

Supplier<Stream<String>> result = input.stream() // Stream<List<String>>
                .<Supplier<Stream<String>>>map(list -> list::stream) // Stream<Supplier<Stream<String>>>

Then reduce this stream of suppliers and produce a cartesian product for the streams of strings that are belong to suppliers like this:

.reduce((sup1, sup2) -> () -> sup1.get().flatMap(e1 -> sup2.get().map(e2 -> e1 + e2)))

Reduce returns optional, so to handle absent value I will return an empty string stream:

.orElse(() -> Stream.of(""));

After all we just need to get the supplier value (which will be a stream of strings) and print it out:

s.get().forEach(System.out::println);

The whole method will look like:

public static void printCartesianProduct(List<String>... lists) {
        List<List<String>> input = asList(lists);
        Supplier<Stream<String>> s = input.stream()
                // Stream<List<String>>
                .<Supplier<Stream<String>>>map(list -> list::stream)
                // Stream<Supplier<Stream<String>>>
                .reduce((sup1, sup2) -> () -> sup1.get()
                        .flatMap(e1 -> sup2.get().map(e2 -> e1 + e2)))
                .orElse(() -> Stream.of(""));

        s.get().forEach(System.out::println);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.