用 Java 8 平面图替换嵌套循环
Replace nested loop with Java 8 flatmap
我正在尝试使用 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
您必须在 flatMap
阶段创建您想要的元素,例如:
xs.stream().flatMap(x -> ys.stream().map(y -> x + " + " + y)).forEach(System.out::println);
一般不需要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。
是的,它看起来很漂亮,但只有这样幼稚的任务。您 create/close 为每个元素创建一个新流,仅将它们合并到结果流中。而这一切只是为了打印出来?
相比之下,forEach
提供了一种没有任何性能成本的单行解决方案(内部标准 foreach
)。
基本上,这是这些列表的笛卡尔积。我会先将它们组合成一个列表:
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)))
减少 return 可选,因此为了处理缺失值,我将 return 一个空字符串流:
.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);
}
我正在尝试使用 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
您必须在 flatMap
阶段创建您想要的元素,例如:
xs.stream().flatMap(x -> ys.stream().map(y -> x + " + " + y)).forEach(System.out::println);
一般不需要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。
是的,它看起来很漂亮,但只有这样幼稚的任务。您 create/close 为每个元素创建一个新流,仅将它们合并到结果流中。而这一切只是为了打印出来?
相比之下,forEach
提供了一种没有任何性能成本的单行解决方案(内部标准 foreach
)。
基本上,这是这些列表的笛卡尔积。我会先将它们组合成一个列表:
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)))
减少 return 可选,因此为了处理缺失值,我将 return 一个空字符串流:
.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);
}