[英]Setting a boolean flag inside Java 8 Stream
我想知道從java流設置布爾標志值的最佳實踐是什么。 這是我想要做的一個例子:
List<Integer> list = Arrays.asList(1,2,3,4,5);
boolean flag = false;
List<Integer> newList = list.stream()
//many other filters, flatmaps, etc...
.filter(i -> {
if(condition(i)){
flag = true;
}
return condition(i);
})
//many other filters, flatmaps, etc...
.collect(Collectors.toList());
//do some work with the new list and the flag
但是,這違反了語言限制“lambda表達式中使用的變量應該是最終的或有效的最終結果”。 我能想到一些解決方案,但我不確定哪種解決方案最好。 我的第一個解決方案是添加匹配condition
元素到列表並檢查List::isEmpty
。 也可以在AtomicReference
包裝flag
。
請注意,我的問題類似於這個問題 ,但我試圖在最后提取一個布爾值而不是設置一個變量。
不要使用完全不相關的任務來修改生成newList
的任務。 只是用
boolean flag = list.stream().anyMatch(i -> condition(i));
其次是其他流代碼。
有兩個典型的反對意見
但這是兩次迭代。
是的,確實如此,但是誰說迭代一次ArrayList
是個問題呢? 不要試圖避免多個流操作,除非您知道您確實有一個昂貴的遍歷流源,如外部文件。 如果您擁有如此昂貴的資源,那么首先將元素收集到集合中可能仍然更容易,您可以遍歷兩次。
但它不止一次地評估condition(…)
。
嗯,實際上它的評估比原始代碼要少
.filter(i -> { if(condition(i)){ flag = true; } return condition(i); })
因為anyMatch
在第一個匹配時停止,而原始謂詞在每個元素上無條件地評估condition(i)
兩次。
如果在條件之前有幾個中間步驟,則可以收集到中間List
List<Integer> intermediate = list.stream()
//many other filters, flatmaps, etc...
.filter(i -> condition(i))
.collect(Collectors.toList());
boolean flag = !intermediate.isEmpty();
List<Integer> newList = intermediate.stream()
//many other filters, flatmaps, etc...
.collect(Collectors.toList());
但更常見的是,中間步驟並不像乍看之下那么昂貴。 類似的中間步驟的性能特征可以根據實際終端操作在不同的流操作中變化。 因此,它可能仍然可以在飛行中充分執行這些步驟:
boolean flag = list.stream()
//many other filters, flatmaps, etc...
.anyMatch(i -> condition(i));
List<Integer> newList = list.stream()
//many other filters, flatmaps, etc...
.filter(i -> condition(i))
//many other filters, flatmaps, etc...
.collect(Collectors.toList());
如果您擔心代碼重復本身,您仍然可以將公共代碼放入流返回實用程序方法中。
只有在非常罕見的情況下,才能進入低級API,並像在這個答案中一樣窺視Stream。 如果你這樣做,你不應該走Iterator
的路線,它將丟失有關內容的元信息,但使用Spliterator
:
Spliterator<Integer> sp = list.stream()
//many other filters, flatmaps, etc...
.filter(i -> condition(i))
.spliterator();
Stream.Builder<Integer> first = Stream.builder();
boolean flag = sp.tryAdvance(first);
List<Integer> newList = Stream.concat(first.build(), StreamSupport.stream(sp, false))
//many other filters, flatmaps, etc...
.collect(Collectors.toList());
請注意,在所有這些情況下,如果flag
為false
,則可以快捷方式,因為結果只能是空列表:
List<Integer> newList = !flag? Collections.emptyList():
/*
subsequent stream operation
*/;
編輯:(基於Holger的評論如下 )
我在這里只是出於歷史目的而回答這個問題;)這是我嘗試使用Iterator
來解決問題,盡管Spliterator
要好得多。 這個答案並非100%錯誤,但是當流轉換為Iterator
時,支持流的分裂器的特征(即SIZED
, ORDERED
等)將丟失。 請參閱Holger關於最佳方法的精彩答案 ,只要其他替代方案和關於這是否值得付出努力的簡短討論。
如果您需要知道流管道中間是否存在過濾條件匹配,您可能需要考慮將流轉換為Iterator
,檢查迭代器是否具有下一個元素,將該值存儲為您的標志,然后從迭代器創建一個新流,最后繼續使用流管道。
在代碼中:
Iterator<Whatever> iterator = list.stream()
// many other filters, flatmaps, etc...
.filter(i -> condition(i))
.iterator();
boolean flag = iterator.hasNext();
然后,從迭代器創建一個新的Stream
:
Stream<Whatever> stream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.NONNULL), // maybe Spliterator.ORDERED?
false);
最后繼續使用流管道:
List<Integer> newList = stream
// many other filters, flatmaps, etc...
.collect(Collectors.toList());
現在你有newList
和flag
可以使用。
分別檢查布爾標志
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> newList = list.stream()
.filter(i -> condition(i))
//many other filters, flatmaps, etc...
.collect(Collectors.toList());
boolean flag = list.stream()
.filter(i -> condition(i))
.findAny()
.isPresent();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.