簡體   English   中英

Java Streams 將 Streams 與供應商連接起來,然后是不同的(惰性求值行為)

[英]Java Streams concat Streams with Supplier followed by distinct (lazy evaluation behavior)

我有一個像這樣的簡單代碼

import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamSupplierVersusConcat {
    public static void main(String[] args) {
         final StreamSupplierVersusConcat clazz = new StreamSupplierVersusConcat();
         clazz.doConcat();                
    }

    private void doConcat(){        
         System.out.println(Stream.concat(buildStreamFromRange(0,1000).get()
                ,buildStreamFromRange(1000,2000).get())
                .anyMatch("1"::equals));                
}

    private Supplier<Stream<String>>buildStreamFromRange(final int start,final int end){
        return ()->IntStream.range(start, end)
                .mapToObj(i->{
                    System.out.println("index At: "+i);
                    return String.valueOf(i);
                });
    }    
}

我知道 concat 是懶惰的,所以當我運行代碼時,我看到它只生成了 2 個很棒的值,但知道 distinct 是一個有狀態的操作,我認為將該方法放在 Stream 管道上它會生成所有值,然后做 anyMatch 方法,但如果我這樣說

    private void doConcat(){        
         System.out.println(Stream.concat(buildStreamFromRange(0,1000).get()
                ,buildStreamFromRange(1000,2000).get())
                .distinct()//ARE ALL THE VALUES GENERATED NOT REQUIRED HERE???
                .anyMatch("1"::equals));                
}

但是有了不同的和沒有它,我得到了相同的回應。

index At: 0
index At: 1
true

我錯過了什么? 我認為 distinct 會在 anyMatch 看到任何項目之前看到消耗所有項目。 在 Java 8 上測試。

非常感謝。

在恢復我的理解中,我認為 distinct 會在 anyMatch 看到任何項目之前看到消耗所有項目。 不正確這個例子解釋了它。

private void distinctIsNotABlockingCall(){
    final boolean match = Stream.of("0","1","2","3","4","5","6","7","8","8","8","9","9","9","9","9","9","9","9","9","10","10","10","10")
            .peek(a->System.out.println("before: "+a))
            .distinct()//I THOUGHT THAT NOT ANYMATCH WAS CALLED AFTER DISTINCT HANDLE ALL THE ITEMS BUT WAS WRONG.
            .peek(a->System.out.println("after: "+a))
            .anyMatch("10"::equals);
    System.out.println("match? = " + match);                
}

before: 0
after: 0
before: 1
after: 1
before: 2
after: 2
before: 3
after: 3
before: 4
after: 4
before: 5
after: 5
before: 6
after: 6
before: 7
after: 7
before: 8
after: 8
before: 8 distinct working
before: 8 distinct working
before: 9
after: 9 
before: 9 distinct working
before: 9 distinct working
before: 9 distinct working
before: 9 distinct working
before: 9 distinct working
before: 9 distinct working
before: 9 distinct working
before: 9 distinct working
before: 10
after: 10
match? = true

您可以看到 distinct 收到重復和非重復的值,而且 anyMatch 正在接收那些非重復的值,並且 distinct 和 anyMatch 同時工作,非常感謝。

流是惰性的,因為除非調用終端操作,否則不會評估中間操作。 在這里檢查SO答案

據我所知,Stream Api 會流式傳輸每個元素,直到應用終端操作,然后流式傳輸下一個元素。

這也將解釋這里的情況。 元素"0"被流式傳輸,終端操作不滿足。 另一個需要流式傳輸, "1"現在,終端操作.anyMatch("1"::equals)); 很滿意。 無需流式傳輸更多元素。 將在兩者之間調用 Distinct 而無需更改流式元素。

因此,如果您在"0"還有另一個"0"要流式傳輸,則它根本不會到達終端操作。

 private void doConcat(){        
     
System.out.println(Stream.concat(buildStreamFromRange(0,1000).get()
                ,buildStreamFromRange(1000,2000).get())
                .distinct()
                .peek( e -> System.out.println(e))
                .anyMatch("1"::equals));  

嘗試添加 peek 並嘗試在開始時流式傳輸 2 個"0"元素。 其中只有 1 個會通過流程並從 peek 打印。

Peek也可用於調試目的,並在您不確定時查看流程的行為,因此將來可以利用它。

給未來讀者的簡單例子:

一個更簡單的例子,未來的讀者將能夠理解流中的惰性運算符是如何工作的,如下所示:

Stream.of("0","0","1","2","3","4")
                .distinct()
                .peek(a->System.out.println("after distinct: "+a))
                .anyMatch("1"::equals);

會打印

after distinct: 0
after distinct: 1

首先"0"一直到終端操作但不滿足它。 必須流式傳輸另一個元素。

第二個"0"通過.distinct()過濾並且永遠不會到達終端操作

由於還沒有滿足終端操作,下一個元素被流式傳輸。

"1"通過終端操作並滿足。

不再需要流式傳輸元素。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM