【问题标题】:Replace nested loop with Java 8 flatmap用 Java 8 flatmap 替换嵌套循环
【发布时间】:2017-04-02 09:52:37
【问题描述】:

我正在尝试使用 flatmap 通过 Stream API 创建一个嵌套循环,但我似乎无法弄清楚。例如,我想重新创建以下循环:

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);

我可以这样做,但这看起来很丑:

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

Flatmap 看起来很有希望,但我如何才能访问外循环中的变量?

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

输出:

*** 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

【问题讨论】:

标签: java loops java-8 java-stream flatmap


【解决方案1】:

您必须在flatMap 阶段创建您想要的元素,例如:

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

【讨论】:

  • 谢谢。在我看来,嵌套的 Stream 选项会更容易使用。在给定的示例中,您可以很容易地将组合操作移动到平面地图,但在许多其他情况下,这会更有效,尤其是在向嵌套添加更多级别时。
  • @wvdz 这总是取决于用例和什么对你来说更易读。
【解决方案2】:

通常不需要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

而且这里不需要 Stream API。

是的,它看起来很漂亮,但只有这样幼稚的任务。您为每个元素创建/关闭一个新流只是为了将它们合并到结果流中。而所有这些只是为了打印出来?

相比之下,forEach 提供了一种没有任何性能成本的单线解决方案(内部有一个标准的foreach)。

【讨论】:

  • 这相当于我在问题中给出的嵌套流示例。
【解决方案3】:

基本上,这是这些列表的笛卡尔积。我会先将它们组合成一个列表:

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);

然后创建一个列表流,每个列表将映射到自己的流并将这些内容保存到Supplier

Supplier<Stream<String>> result = 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)))

Reduce 返回可选的,所以为了处理缺失值,我将返回一个空字符串流:

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

毕竟我们只需要获取供应商值(这将是一个字符串流)并将其打印出来:

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

整个方法如下所示:

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);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-05
    • 1970-01-01
    相关资源
    最近更新 更多