Stream.flatMap() 的递归使用 [英] Recursive use of Stream.flatMap()

查看:34
本文介绍了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 Streams 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆