2017-09-06 26 views
3

在我的過濾器爲真後,有沒有辦法讓我的數據流僅迭代一次?Java流。只有一次

例子:

list.stream() 
    .filter(ele -> ele.isBlue()) 
    .map(Element::getSomething) 
    .collect(toList()); 

說我有我的列表中的許多元素,但只有一個是藍色的。我知道只有一個是藍色的,但我不知道它在哪裏。爲了討論起見,我們可以說isBlue()是一個很慢的方法,所以我想在它碰到一次時停止。

+5

'findFirst()。orElse(null)''而不是調用'collect'。如果您使用'parallelStream'並確保流中只有一個元素,則可以使用'findAny()。orElse(null)'代替。 – Lothar

+2

因爲流是懶惰的,所以你只需要'filter(..)。findFirst()。orElse(.. whatever)' –

回答

7

如果您正在尋找只有一個元素,而不是collect()使用findFirst()/findAny()

Optional<Something> s = list.stream() 
    .filter(ele -> ele.isBlue()) 
    .map(Element::getSomething) 
    .findAny(); 

然後你要麼得到一個Optional含有單一的藍色元素,或空Optional

有一個在簡單流findFirst()findAny()之間沒有差異,只有當並行涉及(其中findFirst()返回確定性的第一元件,和findAny()將返回它找到任何單個合適的元件)。

5

你仍然可以collect,但有一個limit

list.stream() 
    .filter(ele -> ele.isBlue()) 
    .map(Element::getSomething) 
    .limit(1) 
    .collect(toList()); 
+2

如果你做了需要集合的額外工作,這很有用,所以你不需要把內容一個'Optional'手動添加到新列表中。 – Kayaman

+0

感謝您的提示!我想我會先嚐試findFirst()變體。 – Kims

+3

@Kayaman:儘管如此,findFirst()。map(Collections :: singletonList).orElseGet(Collections :: emptyList)並不是那麼複雜,並且有一些整潔的屬性...... – Holger

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以查看所有終止操作。