Recursive use of Stream.flatMap()(递归使用 Stream.flatMap())
问题描述
考虑以下类:
public class Order {
private String id;
private List<Order> orders = new ArrayList<>();
@Override
public String toString() {
return this.id;
}
// getters & setters
}
注意:请务必注意,我无法修改此类,因为我从外部 API 使用它.
NOTE: It is important to note that I cannot modify this class, because I'm consuming it from an external API.
还要考虑以下订单层次结构:
Also consider the following hierarchy of orders:
Order o1 = new Order();
o1.setId("1");
Order o11 = new Order();
o11.setId("1.1");
Order o111 = new Order();
o111.setId("1.1.1");
List<Order> o11Children = new ArrayList<>(Arrays.asList(o111));
o11.setOrders(o11Children);
Order o12 = new Order();
o12.setId("1.2");
List<Order> o1Children = new ArrayList<>(Arrays.asList(o11, o12));
o1.setOrders(o1Children);
Order o2 = new Order();
o2.setId("2");
Order o21 = new Order();
o21.setId("2.1");
Order o22 = new Order();
o22.setId("2.2");
Order o23 = new Order();
o23.setId("2.3");
List<Order> o2Children = new ArrayList<>(Arrays.asList(o21, o22, o23));
o2.setOrders(o2Children);
List<Order> orders = new ArrayList<>(Arrays.asList(o1, o2));
可以用这种方式直观地表示:
Which could be visually represented this way:
1
1.1
1.1.1
1.2
2
2.1
2.2
2.3
现在,我想将这个订单层次结构扁平化为一个List
,这样我就可以得到以下信息:
Now, I want to flatten this hierarchy of orders into a List
, so that I get the following:
[1, 1.1, 1.1.1, 1.2, 2, 2.1, 2.2, 2.3]
我已经设法通过递归地使用 flatMap()
(以及一个辅助类)来做到这一点,如下所示:
I've managed to do it by recursively using flatMap()
(along with a helper class), as follows:
List<Order> flattened = orders.stream()
.flatMap(Helper::flatten)
.collect(Collectors.toList());
这是辅助类:
public final class Helper {
private Helper() {
}
public static Stream<Order> flatten(Order order) {
return Stream.concat(
Stream.of(order),
order.getOrders().stream().flatMap(Helper::flatten)); // recursion here
}
}
下面一行:
System.out.println(flattened);
产生以下输出:
[1, 1.1, 1.1.1, 1.2, 2, 2.1, 2.2, 2.3]
到目前为止一切顺利.结果绝对正确.
So far so good. The result is absolutely correct.
但是,阅读后这个问题,我对在递归方法中使用 flatMap()
有一些担忧.特别是,我想知道流是如何扩展的(如果这是术语).所以我修改了 Helper
类并使用 peek(System.out::println)
来检查这个:
However, after reading this question, I had some concerns regarding the usage of flatMap()
within a recursive method. Particularly, I wanted to know how the stream was being expanded (if that's the term). So I modified the Helper
class and used peek(System.out::println)
to check this:
public static final class Helper {
private Helper() {
}
public static Stream<Order> flatten(Order order) {
return Stream.concat(
Stream.of(order),
order.getOrders().stream().flatMap(Helper::flatten))
.peek(System.out::println);
}
}
输出是:
1
1.1
1.1
1.1.1
1.1.1
1.1.1
1.2
1.2
2
2.1
2.1
2.2
2.2
2.3
2.3
我不确定这是否是应该打印的输出.
I'm not sure if this is the output that should be printed.
所以,我想知道是否可以让中间流包含重复的元素.此外,这种方法的优缺点是什么?毕竟,以这种方式使用 flatMap()
是否正确?有没有更好的方法来达到同样的效果?
So, I wonder if it's OK to let intermediate streams contain repeated elements. Furthermore, what are the pros and cons of this approach? Is it correct, after all, to use flatMap()
this way? Is there a better way to achieve the same?
推荐答案
嗯,我在一个通用的 Tree
类中使用了相同的模式,并且对它没有错误的感觉.唯一的区别是,Tree
类本身提供了一个 children()
和 allDescendants()
方法,它们都返回一个 Stream
和后者建立在前者之上.这与 我应该返回集合还是流?" 和 命名返回流的java方法".
Well, I used the same pattern with a generic Tree
class and didn’t have a wrong feeling with it. The only difference is, that the Tree
class itself offered a children()
and allDescendants()
methods, both returning a Stream
and the latter building on the former. This is related to "Should I return a Collection or a Stream?" and "Naming java methods that return streams".
从 Stream
的角度来看,flatMap
到不同类型的子节点(即在遍历属性时)与 flatMap
给同类型的孩子.如果返回的流再次包含相同的元素也没有问题,因为流的元素之间没有关系.原则上,您可以使用 flatMap
作为 filter
操作,使用模式 flatMap(x -> condition? Stream.of(x): Stream.empty())代码>.也可以使用它来复制 this answer 中的元素.
From a Stream
s perspective, there is no difference between a flatMap
to children of a different type (i.e. when traversing a property) and a flatMap
to children of the same type. There is also no problem if the returned stream contains the same element again, as there is no relationship between the elements of the streams. In principle, you can use flatMap
as a filter
operation, using the pattern flatMap(x -> condition? Stream.of(x): Stream.empty())
. It’s also possible to use it to duplicate elements like in this answer.
这篇关于递归使用 Stream.flatMap()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:递归使用 Stream.flatMap()
基础教程推荐
- FirebaseListAdapter 不推送聊天应用程序的单个项目 - Firebase-Ui 3.1 2022-01-01
- 设置 bean 时出现 Nullpointerexception 2022-01-01
- Java:带有char数组的println给出乱码 2022-01-01
- 降序排序:Java Map 2022-01-01
- Java Keytool 导入证书后出错,"keytool error: java.io.FileNotFoundException &拒绝访问" 2022-01-01
- “未找到匹配项"使用 matcher 的 group 方法时 2022-01-01
- 如何使用 Java 创建 X509 证书? 2022-01-01
- 无法使用修饰符“public final"访问 java.util.Ha 2022-01-01
- 在 Libgdx 中处理屏幕的正确方法 2022-01-01
- 减少 JVM 暂停时间 >1 秒使用 UseConcMarkSweepGC 2022-01-01