Java8中使用流嵌套列表

47
我有一个对象 A 的列表。这个列表中的每个对象 A 包含一个对象 B 的列表,并且对象 B 包含一个对象 C 的列表。我想使用 Java 8 中的流来过滤具有属性名称的对象 C,以下是如何使用流编写代码以避免嵌套循环的方法:
C c1 = null;
String name = "name1"
for (A a: listOfAObjects) {
    for (B b: a.getList()) {
        for (C c: b.getPr()) {
            if (c.getName().equalsIgnoreCase(name)) {
                c1= c;
                break;
            }
        }
    }
}
4个回答

64
你可以使用两个flatMap,然后是一个filter,接着你可以选择第一个结果,如果没有结果则返回null
C c1 = listOfAObjects.stream()
        .flatMap(a -> a.getList().stream())
        .flatMap(b -> b.getPr().stream())
        .filter(c -> c.getName().equalsIgnoreCase(name))
        .findFirst()
        .orElse(null);

1
flatMap 在 Java 10 之前是急切的,不像 for 循环。 - Eugene
1
@Eugene,它仍然在第一个匹配的A处停止,只是子列表的处理没有短路。 - Holger
@sk555,你能展示一下你的完整代码以及如何测试它吗? - Youcef LAIDANI
2
@sk555 如果你得到了一个 null,那很可能是 orElse 被触发了,这意味着在你的列表中没有这样的元素,就这么简单。 - Eugene
@YCF_L 我把它作为正确答案放了,因为它在分离的示例中有效。稍后我会分析一下为什么它在实际代码中不起作用。非常感谢。 - sk555
显示剩余2条评论

16
你可以使用 flatMap 来完成它。
我用一个包含 Person 列表的 Company 的示例进行了说明。
public static void main(String[] args) {
    List<Company> companies = Arrays.asList(
            new Company(Arrays.asList(new Person("Jon Skeet"), new Person("Linus Torvalds"))),
            new Company(Arrays.asList(new Person("Dennis Ritchie"), new Person("Bjarne Stroustrup"))),
            new Company(Arrays.asList(new Person("James Gosling"), new Person("Patrick Naughton")))
    );

    List<String> persons = companies.stream()
            .flatMap(company -> company.getPersons().stream())
            .map(Person::getName)
            .collect(Collectors.toList());

    System.out.println(persons);
}

输出:

[Jon Skeet,Linus Torvalds,Dennis Ritchie,Bjarne Stroustrup,James Gosling,Patrick Naughton]


这是正确的方法,但结果不同:在所有嵌套循环结束时,您应该只剩下最后一个“c”对象,其中包含所有嵌套列表中的“name”。 - GDC
1
@GDC 我同意。据我理解,OP的问题是“如何从2个嵌套的集合中创建一个流”,因此我只是回答了这个问题。 - Arnaud Denoyelle

7
 listOfObjectsA.stream()
               .flatMap(a -> a.getListOfObjectsB.stream())
               .flatMap(b -> b.getListOfObjectsC().stream())
               .filter(c -> name.equals(c.getName()))
               .findAny()
               .orElse(....)

以上指定的那个有什么不同? - sk555
1
@sk555 没有什么区别...这里的区别在于我使用了findAny而不是findFirst - 在当前顺序流的实现下,它们的作用相同;但是在并行流中 - 你可能想要findFirst - 因为它会按照遇到的顺序获取第一个元素。问题是 - 如果你关心要取哪个(只要它符合你的条件)- 你可能想要findAny - Eugene

5

我曾经有同样的任务,但是我有一个嵌套类。而且我必须使用嵌套集合中的筛选器来过滤对象。结果,我必须得到在集合中有匹配项的项目。

例如:

public class RootElement {
    private String name;
    private List<String> nestedElements;

    //getters / setters and constructors
}

初始化集合并添加元素:

List<RootElement> elements = Arrays.asList(
                new RootElement("first", Arrays.asList("one", "two", "three")),
                new RootElement("second", Arrays.asList("four", "one", "two")));

筛选器示例:

String filterParam = "four";
        List<RootElement> filtered = elements.stream()
                .flatMap(root -> root.getNestedElements()
                        .stream()
                        .filter(nested -> nested.equalsIgnoreCase(filterParam))
                        .map(filteredElement -> new RootElement(root.getName(), root.getNestedElement())))
                .collect(Collectors.toList());

希望能帮助到某些人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接