Java 8 Stream 等價物

關於懶惰

如果你想延遲處理一個鏈,你可以在鏈之前使用 asSequence() 轉換為 Sequence。在功能鏈的末尾,你通常也會得到一個 Sequence。然後你可以使用 toList()toSet()toMap() 或其他一些功能在最後實現 Sequence

// switch to and from lazy
val someList = items.asSequence().filter { ... }.take(10).map { ... }.toList()

// switch to lazy, but sorted() brings us out again at the end
val someList = items.asSequence().filter { ... }.take(10).map { ... }.sorted()

為什麼沒有型別?!?

你會注意到 Kotlin 示例未指定型別。這是因為 Kotlin 具有完整的型別推斷並且在編譯時完全是型別安全的。比 Java 更多,因為它也有可空型別,可以幫助防止可怕的 NPE。所以這在 Kotlin:

val someList = people.filter { it.age <= 30 }.map { it.name }

是相同的:

val someList: List<String> = people.filter { it.age <= 30 }.map { it.name }

因為 Kotlin 知道 people 是什麼,並且 people.ageInt 因此過濾器表示式只允許與 Int 進行比較,並且 people.nameString 因此 map 步驟產生 List<String>(讀取 List of String)。

現在,如果 people 可能是 null,那麼在一個 List<People>? 然後:

val someList = people?.filter { it.age <= 30 }?.map { it.name }

返回需要進行空檢查的 List<String>?或使用其他 Kotlin 運算子之一獲取可空值,請參閱此 Kotlin 慣用方法來處理可空值以及處理可空或空列表的 慣用方法

重用流

在 Kotlin 中,它取決於收集的型別是否可以不止一次消費。Sequence 每次都會生成一個新的迭代器,除非它斷言只使用一次,否則它每次執行時都會重置為開始。因此,雖然以下在 Java 8 流中失敗,但在 Kotlin 中工作:

// Java:
Stream<String> stream =
Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> s.startsWith("b"));

stream.anyMatch(s -> true);    // ok
stream.noneMatch(s -> true);   // exception
// Kotlin:  
val stream = listOf("d2", "a2", "b1", "b3", "c").asSequence().filter { it.startsWith('b' ) }

stream.forEach(::println) // b1, b2

println("Any B ${stream.any { it.startsWith('b') }}") // Any B true
println("Any C ${stream.any { it.startsWith('c') }}") // Any C false

stream.forEach(::println) // b1, b2

並在 Java 中獲得相同的行為:

// Java:
Supplier<Stream<String>> streamSupplier =
    () -> Stream.of("d2", "a2", "b1", "b3", "c")
          .filter(s -> s.startsWith("a"));

streamSupplier.get().anyMatch(s -> true);   // ok
streamSupplier.get().noneMatch(s -> true);  // ok

因此,在 Kotlin 中,資料提供者決定它是否可以重置並提供新的迭代器。但是如果你想故意將 Sequence 約束到一次迭代,你可以使用 constrainOnce() 函式為 Sequence,如下所示:

val stream = listOf("d2", "a2", "b1", "b3", "c").asSequence().filter { it.startsWith('b' ) }
        .constrainOnce()

stream.forEach(::println) // b1, b2
stream.forEach(::println) // Error:java.lang.IllegalStateException: This sequence can be consumed only once. 

也可以看看: