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. 

也可以看看: