Java修炼终极指南:185 使用 Stream 过滤嵌套集合
这是面试中的一个经典问题,通常从一个模型开始,如下所示(我们假设集合是一个 List):
public class Author {
private final String name;
private final List<Book> books;
...
}
public class Book {
private final String title;
private final LocalDate published;
...
}
假设有一个 List<Author> 表示为 authors,编写一个流管道,返回在 2002 年出版的 List<Book>。你已经应该认识到这是一个典型的 flatMap() 问题,所以没有更多的细节,我们可以这样写:
List<Book> book2002fm = authors.stream()
.flatMap(author -> author.getBooks().stream())
.filter(book -> book.getPublished().getYear() == 2002)
.collect(Collectors.toList());
从问题 178 中,我们知道 flatMap() 有用的地方,我们应该考虑 JDK 16 的 mapMulti()。在检查以下代码片段之前,挑战自己通过 mapMulti() 重写之前的代码:
List<Book> book2002mm = authors.stream()
.<Book>mapMulti((author, consumer) -> {
for (Book book : author.getBooks()) {
if (book.getPublished().getYear() == 2002) {
consumer.accept(book);
}
}
})
.collect(Collectors.toList());
好的,这非常清楚!那么找到在 2002 年出版书籍的 List<Author> 怎么样?当然,mapMulti() 可以再次帮助我们。我们所要做的就是循环书籍,当我们找到一个在 2002 年出版的书时,我们简单地将作者传递给消费者,而不是书。此外,在将作者传递给消费者之后,我们可以打破当前作者的循环,继续下一个:
List<Author> author2002mm = authors.stream()
.<Author>mapMulti((author, consumer) -> {
for (Book book : author.getBooks()) {
if (book.getPublished().getYear() == 2002) {
consumer.accept(author);
break;
}
}
})
.collect(Collectors.toList());
另一种方法可以依赖 anyMatch() 和一个谓词,该谓词产生一个在 2002 年出版的书籍的流,如下所示:
List<Author> authors2002am = authors.stream()
.filter(
author -> author.getBooks()
.stream()
.anyMatch(book -> book.getPublished()
.getYear() == 2002)
)
.collect(Collectors.toList());
通常情况下,我们不想改变给定的列表,但如果这不是一个问题(或者,正是我们想要的),那么我们可以直接在 List<Author> 上依赖 removeIf() 来实现同样的结果:
authors.removeIf(author -> author.getBooks().stream()
.noneMatch(book -> book.getPublished().getYear() == 2002));
完成了!现在,在你的面试中,你应该不会因为这类问题而遇到问题。