在我的过滤器为真后,有没有办法让我的流只迭代一次?
示例:
list.stream()
.filter(ele -> ele.isBlue())
.map(Element::getSomething)
.collect(toList());
说我的列表中有很多元素,但只有一个是蓝色的。我知道只有一个是蓝色的,但我不知道它在哪里。为了论证,让我们说isBlue()是一个缓慢的方法,所以我想在它碰到一次时停止。
答案 0 :(得分:7)
如果您只查找一个元素,而不是collect()
使用findFirst()
/ findAny()
。
Optional<Something> s = list.stream()
.filter(ele -> ele.isBlue())
.map(Element::getSomething)
.findAny();
然后您可以获得包含单个蓝色元素的Optional
或空Optional
。
简单流中的findFirst()
和findAny()
之间没有区别,只有在涉及并行性时(findFirst()
确定性地返回第一个元素,findAny()
将返回它找到的任何一个合适的元素。
答案 1 :(得分:5)
您仍可collect
,但limit
:
list.stream()
.filter(ele -> ele.isBlue())
.map(Element::getSomething)
.limit(1)
.collect(toList());
答案 2 :(得分:3)
值得理解的是,流管道是由最后一步驱动的,终止操作。
没有想到一个流生产商将物品沿着传送带推入等待的消费者,而是想到拉动物品的终止操作 - 生产者在被问到时会添加新物品。
在您的示例中,终止操作是collect(collector)
- 这将从上一步骤中拉出项目,直到不再有,将每个元素提交给收集器。上一步是map()
,但它可以按需运行 - 它只会从之前的步骤中提取项目 - filter()
- 当collect()
要求另一项时。
findFirst()
是另一个终止操作。它向上一步询问一个项目 - 流中的第一个项目。因此,如果我们将您的示例更改为...
list.stream()
.filter(ele -> ele.isBlue())
.map(Element::getSomething)
.findFirst(toList());
... findFirst()
管道步骤将从map()
步骤中读取一次。如果它获得了一个项目,它将返回Optional.of(item)
。
map()
反过来会从filter()
读取一次。 filter()
将重复读取流,直到isBlue()
为真,然后返回。
如果filter()
到达其输入流的末尾而未找到匹配项,则它将指示流结束。这将沿着管道向下传播,findFirst()
将注意到流末尾,并返回Optional.empty()
。
还有findAny()
,它将返回任意匹配项。如果管道的一部分被并行化,这可能会更快地返回 - 或者当有一个顺序敏感的步骤阻止它时,它可能允许管道并行化。
查看Stream
Javadoc以查看所有终止操作。