Stream.flatMap() 的递归使用 [英] Recursive use of 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<之间没有区别/code> 到相同类型的孩子.如果返回的流再次包含相同的元素也没有问题,因为流的元素之间没有关系.原则上,您可以使用
flatMap
作为 filter
操作,使用模式 flatMap(x -> condition? Stream.of(x): Stream.empty())
.也可以使用它来复制这个答案中的元素.
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() 的递归使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!