标准 Kotlin 库中有哪些 Java 8 Stream.collect 等效项?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34642254/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What Java 8 Stream.collect equivalents are available in the standard Kotlin library?
提问by Jayson Minard
In Java 8, there is Stream.collect
which allows aggregations on collections. In Kotlin, this does not exist in the same way, other than maybe as a collection of extension functions in the stdlib. But it isn't clear what the equivalences are for different use cases.
在 Java 8 中,Stream.collect
允许对集合进行聚合。在 Kotlin 中,这不以相同的方式存在,除了可能作为 stdlib 中的扩展函数集合。但尚不清楚不同用例的等效项是什么。
For example, at the top of the JavaDoc for Collectors
are examples written for Java 8, and when porting them to Kolin you can't use the Java 8 classes when on a different JDK version, so likely they should be written differently.
例如,在JavaDoc forCollectors
的顶部是为 Java 8 编写的示例,当将它们移植到 Kolin 时,您不能在不同的 JDK 版本上使用 Java 8 类,因此它们可能应该以不同的方式编写。
In terms of resources online showing examples of Kotlin collections, they are typically trivial and don't really compare to the same use cases. What are good examples that really match the cases such as documented for Java 8 Stream.collect
? The list there is:
就显示 Kotlin 集合示例的在线资源而言,它们通常是微不足道的,并不能真正与相同的用例进行比较。什么是真正匹配 Java 8 记录的案例的好例子Stream.collect
?那里的名单是:
- Accumulate names into a List
- Accumulate names into a TreeSet
- Convert elements to strings and concatenate them, separated by commas
- Compute sum of salaries of employee
- Group employees by department
- Compute sum of salaries by department
- Partition students into passing and failing
- 将名称累积到列表中
- 将名称累积到 TreeSet 中
- 将元素转换为字符串并将它们连接起来,用逗号分隔
- 计算员工工资总额
- 按部门分组员工
- 按部门计算工资总和
- 将学生分为合格和不合格
With details in the JavaDoc linked above.
上面链接的 JavaDoc 中有详细信息。
Note:this question is intentionally written and answered by the author (Self-Answered Questions), so that the idiomatic answers to commonly asked Kotlin topics are present in SO. Also to clarify some really old answers written for alphas of Kotlin that are not accurate for current-day Kotlin.
注意:这个问题是作者特意写的和回答的(自我回答的问题),所以常见的 Kotlin 主题的惯用答案都在 SO 中。还要澄清一些为 Kotlin alphas 编写的非常旧的答案,这些答案对于当前的 Kotlin 来说是不准确的。
采纳答案by Jayson Minard
There are functions in the Kotlin stdlib for average, count, distinct,filtering, finding, grouping, joining, mapping, min, max, partitioning, slicing, sorting, summing, to/from arrays, to/from lists, to/from maps, union, co-iteration, all the functional paradigms, and more. So you can use those to create little 1-liners and there is no need to use the more complicated syntax of Java 8.
Kotlin 标准库中有函数用于求平均值、计数、不同、过滤、查找、分组、连接、映射、最小值、最大值、分区、切片、排序、求和、到/来自数组、到/从列表、到/从地图、联合、共同迭代、所有功能范式等等。所以你可以使用它们来创建小的 1-liners,并且不需要使用 Java 8 的更复杂的语法。
I think the only thing missing from the built-in Java 8 Collectors
class is summarization (but in another answer to this questionis a simple solution).
我认为内置 Java 8Collectors
类中唯一缺少的是总结(但在这个问题的另一个答案中是一个简单的解决方案)。
One thing missing from both is batching by count, which is seen in another Stack Overflow answerand has a simple answer as well. Another interesting case is this one also from Stack Overflow: Idiomatic way to spilt sequence into three lists using Kotlin. And if you want to create something like Stream.collect
for another purpose, see Custom Stream.collect in Kotlin
两者都缺少的一件事是按计数进行批处理,这可以在另一个 Stack Overflow 答案中看到,并且也有一个简单的答案。另一个有趣的案例来自 Stack Overflow:使用 Kotlin 将序列溢出到三个列表中的惯用方法。如果你想Stream.collect
为其他目的创建类似的东西,请参阅Kotlin 中的 Custom Stream.collect
EDIT 11.08.2017:Chunked/windowed collection operations were added in kotlin 1.2 M2, see https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
编辑 11.08.2017:在 kotlin 1.2 M2 中添加了分块/窗口化集合操作,请参阅https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
It is always good to explore the API Reference for kotlin.collectionsas a whole before creating new functions that might already exist there.
在创建可能已经存在的新函数之前,从整体上探索kotlin.collections的API 参考总是好的。
Here are some conversions from Java 8 Stream.collect
examples to the equivalent in Kotlin:
以下是从 Java 8Stream.collect
示例到 Kotlin 中等效项的一些转换:
Accumulate names into a List
将名称累积到列表中
// Java:
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
// Kotlin:
val list = people.map { it.name } // toList() not needed
Convert elements to strings and concatenate them, separated by commas
将元素转换为字符串并将它们连接起来,用逗号分隔
// Java:
String joined = things.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
// Kotlin:
val joined = things.joinToString(", ")
Compute sum of salaries of employee
计算员工工资总额
// Java:
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val total = employees.sumBy { it.salary }
Group employees by department
按部门分组员工
// Java:
Map<Department, List<Employee>> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// Kotlin:
val byDept = employees.groupBy { it.department }
Compute sum of salaries by department
按部门计算工资总和
// Java:
Map<Department, Integer> totalByDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val totalByDept = employees.groupBy { it.dept }.mapValues { it.value.sumBy { it.salary }}
Partition students into passing and failing
将学生分为合格和不合格
// Java:
Map<Boolean, List<Student>> passingFailing =
students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
// Kotlin:
val passingFailing = students.partition { it.grade >= PASS_THRESHOLD }
Names of male members
男性成员姓名
// Java:
List<String> namesOfMaleMembers = roster
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.map(p -> p.getName())
.collect(Collectors.toList());
// Kotlin:
val namesOfMaleMembers = roster.filter { it.gender == Person.Sex.MALE }.map { it.name }
Group names of members in roster by gender
名册中成员的组名(按性别)
// Java:
Map<Person.Sex, List<String>> namesByGender =
roster.stream().collect(
Collectors.groupingBy(
Person::getGender,
Collectors.mapping(
Person::getName,
Collectors.toList())));
// Kotlin:
val namesByGender = roster.groupBy { it.gender }.mapValues { it.value.map { it.name } }
Filter a list to another list
将列表过滤到另一个列表
// Java:
List<String> filtered = items.stream()
.filter( item -> item.startsWith("o") )
.collect(Collectors.toList());
// Kotlin:
val filtered = items.filter { it.startsWith('o') }
Finding shortest string a list
查找最短字符串列表
// Java:
String shortest = items.stream()
.min(Comparator.comparing(item -> item.length()))
.get();
// Kotlin:
val shortest = items.minBy { it.length }
Counting items in a list after filter is applied
应用过滤器后计算列表中的项目
// Java:
long count = items.stream().filter( item -> item.startsWith("t")).count();
// Kotlin:
val count = items.filter { it.startsWith('t') }.size
// but better to not filter, but count with a predicate
val count = items.count { it.startsWith('t') }
and on it goes... In all cases, no special fold, reduce, or other functionality was required to mimic Stream.collect
. If you have further use cases, add them in comments and we can see!
然后继续......在所有情况下,都不需要特殊的折叠、减少或其他功能来模仿Stream.collect
. 如果您有更多用例,请在评论中添加它们,我们可以看到!
About laziness
关于懒惰
If you want to lazy process a chain, you can convert to a Sequence
using asSequence()
before the chain. At the end of the chain of functions, you usually end up with a Sequence
as well. Then you can use toList()
, toSet()
, toMap()
or some other function to materialize the Sequence
at the end.
如果你想延迟处理一个链,你可以在链之前转换为Sequence
using asSequence()
。在函数链的末尾,通常Sequence
也会以 a 结尾。然后你可以使用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()
Why are there no Types?!?
为什么没有类型?!?
You will notice the Kotlin examples do not specify the types. This is because Kotlin has full type inference and is completely type safe at compile time. More so than Java because it also has nullable types and can help prevent the dreaded NPE. So this in Kotlin:
您会注意到 Kotlin 示例没有指定类型。这是因为 Kotlin 具有完整的类型推断并且在编译时是完全类型安全的。比 Java 更重要,因为它还具有可为空类型,并且可以帮助防止可怕的 NPE。所以在 Kotlin 中:
val someList = people.filter { it.age <= 30 }.map { it.name }
is the same as:
是相同的:
val someList: List<String> = people.filter { it.age <= 30 }.map { it.name }
Because Kotlin knows what people
is, and that people.age
is Int
therefore the filter expression only allows comparison to an Int
, and that people.name
is a String
therefore the map
step produces a List<String>
(readonly List
of String
).
因为科特林知道什么people
是,这people.age
是Int
因此,过滤器表达式只允许比较的Int
,那people.name
是一个String
因此map
步骤产生List<String>
(只读List
的String
)。
Now, if people
were possibly null
, as-in a List<People>?
then:
现在,如果people
可能的话null
,就像List<People>?
那么:
val someList = people?.filter { it.age <= 30 }?.map { it.name }
Returns a List<String>?
that would need to be null checked (or use one of the other Kotlin operators for nullable values, see this Kotlin idiomatic way to deal with nullable valuesand also Idiomatic way of handling nullable or empty list in Kotlin)
返回一个List<String>?
需要进行空检查的(或使用其他 Kotlin 运算符之一来处理可空值,请参阅此Kotlin 惯用方法来处理可空值以及在 Kotlin 中处理可空值或空列表的惯用方法)
See also:
也可以看看:
- API Reference for extension functions for Iterable
- API reference for extension functions for Array
- API reference for extension functions for List
- API reference for extension functions to Map
- Iterable 扩展函数的API 参考
- Array 扩展函数的API 参考
- List 扩展函数的API 参考
- 扩展函数到 Map 的API 参考
回答by Jayson Minard
For additional examples, here are all the samples from Java 8 Stream Tutorialconverted to Kotlin. The title of each example, is derived from the source article:
有关其他示例,这里是Java 8 Stream 教程中转换为 Kotlin 的所有示例。每个示例的标题都来自源文章:
How streams work
流的工作原理
// Java:
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
// C1
// C2
// Kotlin:
val list = listOf("a1", "a2", "b1", "c2", "c1")
list.filter { it.startsWith('c') }.map (String::toUpperCase).sorted()
.forEach (::println)
Different Kinds of Streams #1
不同类型的流 #1
// Java:
Arrays.asList("a1", "a2", "a3")
.stream()
.findFirst()
.ifPresent(System.out::println);
// Kotlin:
listOf("a1", "a2", "a3").firstOrNull()?.apply(::println)
or, create an extension function on String called ifPresent:
或者,在 String 上创建一个名为 ifPresent 的扩展函数:
// Kotlin:
inline fun String?.ifPresent(thenDo: (String)->Unit) = this?.apply { thenDo(this) }
// now use the new extension function:
listOf("a1", "a2", "a3").firstOrNull().ifPresent(::println)
See also: apply()
function
另见:apply()
函数
See also: Extension Functions
另见:扩展函数
See also: ?.
Safe Call operator, and in general nullability: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them
另请参阅: ?.
安全调用运算符,以及一般可空性:在 Kotlin 中,处理可为空值、引用或转换它们的惯用方法是什么
Different Kinds of Streams #2
不同类型的流 #2
// Java:
Stream.of("a1", "a2", "a3")
.findFirst()
.ifPresent(System.out::println);
// Kotlin:
sequenceOf("a1", "a2", "a3").firstOrNull()?.apply(::println)
Different Kinds of Streams #3
不同类型的流#3
// Java:
IntStream.range(1, 4).forEach(System.out::println);
// Kotlin: (inclusive range)
(1..3).forEach(::println)
Different Kinds of Streams #4
不同类型的流#4
// Java:
Arrays.stream(new int[] {1, 2, 3})
.map(n -> 2 * n + 1)
.average()
.ifPresent(System.out::println); // 5.0
// Kotlin:
arrayOf(1,2,3).map { 2 * it + 1}.average().apply(::println)
Different Kinds of Streams #5
不同类型的流 #5
// Java:
Stream.of("a1", "a2", "a3")
.map(s -> s.substring(1))
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println); // 3
// Kotlin:
sequenceOf("a1", "a2", "a3")
.map { it.substring(1) }
.map(String::toInt)
.max().apply(::println)
Different Kinds of Streams #6
不同类型的流 #6
// Java:
IntStream.range(1, 4)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);
// a1
// a2
// a3
// Kotlin: (inclusive range)
(1..3).map { "a$it" }.forEach(::println)
Different Kinds of Streams #7
不同类型的流 #7
// Java:
Stream.of(1.0, 2.0, 3.0)
.mapToInt(Double::intValue)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);
// a1
// a2
// a3
// Kotlin:
sequenceOf(1.0, 2.0, 3.0).map(Double::toInt).map { "a$it" }.forEach(::println)
Why Order Matters
为什么订单很重要
This section of the Java 8 Stream Tutorial is the same for Kotlin and Java.
Java 8 Stream 教程的这一部分对于 Kotlin 和 Java 是相同的。
Reusing Streams
重用流
In Kotlin, it depends on the type of collection whether it can be consumed more than once. A Sequence
generates a new iterator every time, and unless it asserts "use only once" it can reset to the start each time it is acted upon. Therefore while the following fails in Java 8 stream, but works in Kotlin:
在 Kotlin 中,是否可以多次消费取决于集合的类型。ASequence
每次都会生成一个新的迭代器,除非它断言“仅使用一次”,否则每次对其执行操作时都可以重置为开始。因此,虽然以下在 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
And in Java to get the same behavior:
并在 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
Therefore in Kotlin the provider of the data decides if it can reset back and provide a new iterator or not. But if you want to intentionally constrain a Sequence
to one time iteration, you can use constrainOnce()
function for Sequence
as follows:
因此,在 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.
Advanced Operations
高级操作
Collect example #5(yes, I skipped those already in the other answer)
收集示例#5(是的,我跳过了其他答案中已经存在的那些)
// Java:
String phrase = persons
.stream()
.filter(p -> p.age >= 18)
.map(p -> p.name)
.collect(Collectors.joining(" and ", "In Germany ", " are of legal age."));
System.out.println(phrase);
// In Germany Max and Peter and Pamela are of legal age.
// Kotlin:
val phrase = persons.filter { it.age >= 18 }.map { it.name }
.joinToString(" and ", "In Germany ", " are of legal age.")
println(phrase)
// In Germany Max and Peter and Pamela are of legal age.
And as a side note, in Kotlin we can create simple data classesand instantiate the test data as follows:
作为旁注,在 Kotlin 中,我们可以创建简单的数据类并按如下方式实例化测试数据:
// Kotlin:
// data class has equals, hashcode, toString, and copy methods automagically
data class Person(val name: String, val age: Int)
val persons = listOf(Person("Tod", 5), Person("Max", 33),
Person("Frank", 13), Person("Peter", 80),
Person("Pamela", 18))
Collect example #6
收集示例 #6
// Java:
Map<Integer, String> map = persons
.stream()
.collect(Collectors.toMap(
p -> p.age,
p -> p.name,
(name1, name2) -> name1 + ";" + name2));
System.out.println(map);
// {18=Max, 23=Peter;Pamela, 12=David}
Ok, a more interest case here for Kotlin. First the wrong answers to explore variations of creating a Map
from a collection/sequence:
好的,这里有一个更有趣的 Kotlin 案例。首先是探索Map
从集合/序列创建 a 的变体的错误答案:
// Kotlin:
val map1 = persons.map { it.age to it.name }.toMap()
println(map1)
// output: {18=Max, 23=Pamela, 12=David}
// Result: duplicates overridden, no exception similar to Java 8
val map2 = persons.toMap({ it.age }, { it.name })
println(map2)
// output: {18=Max, 23=Pamela, 12=David}
// Result: same as above, more verbose, duplicates overridden
val map3 = persons.toMapBy { it.age }
println(map3)
// output: {18=Person(name=Max, age=18), 23=Person(name=Pamela, age=23), 12=Person(name=David, age=12)}
// Result: duplicates overridden again
val map4 = persons.groupBy { it.age }
println(map4)
// output: {18=[Person(name=Max, age=18)], 23=[Person(name=Peter, age=23), Person(name=Pamela, age=23)], 12=[Person(name=David, age=12)]}
// Result: closer, but now have a Map<Int, List<Person>> instead of Map<Int, String>
val map5 = persons.groupBy { it.age }.mapValues { it.value.map { it.name } }
println(map5)
// output: {18=[Max], 23=[Peter, Pamela], 12=[David]}
// Result: closer, but now have a Map<Int, List<String>> instead of Map<Int, String>
And now for the correct answer:
现在来看看正确答案:
// Kotlin:
val map6 = persons.groupBy { it.age }.mapValues { it.value.joinToString(";") { it.name } }
println(map6)
// output: {18=Max, 23=Peter;Pamela, 12=David}
// Result: YAY!!
We just needed to join the matching values to collapse the lists and provide a transformer to jointToString
to move from Person
instance to the Person.name
.
我们只需要加入匹配的值来折叠列表并提供一个转换器来jointToString
从Person
实例移动到Person.name
.
Collect example #7
收集示例 #7
Ok, this one can easily be done without a custom Collector
, so let's solve it the Kotlin way, then contrive a new example that shows how to do a similar process for Collector.summarizingInt
which does not natively exist in Kotlin.
好的,无需自定义即可轻松完成此操作Collector
,因此让我们以 Kotlin 方式解决它,然后设计一个新示例,展示如何执行Collector.summarizingInt
Kotlin 中不存在的类似过程。
// Java:
Collector<Person, StringJoiner, String> personNameCollector =
Collector.of(
() -> new StringJoiner(" | "), // supplier
(j, p) -> j.add(p.name.toUpperCase()), // accumulator
(j1, j2) -> j1.merge(j2), // combiner
StringJoiner::toString); // finisher
String names = persons
.stream()
.collect(personNameCollector);
System.out.println(names); // MAX | PETER | PAMELA | DAVID
// Kotlin:
val names = persons.map { it.name.toUpperCase() }.joinToString(" | ")
It's not my fault they picked a trivial example!!!Ok, here is a new summarizingInt
method for Kotlin and a matching sample:
他们挑了一个微不足道的例子不是我的错!!!好的,这是summarizingInt
Kotlin的新方法和匹配示例:
SummarizingInt Example
SummarizingInt 示例
// Java:
IntSummaryStatistics ageSummary =
persons.stream()
.collect(Collectors.summarizingInt(p -> p.age));
System.out.println(ageSummary);
// IntSummaryStatistics{count=4, sum=76, min=12, average=19.000000, max=23}
// Kotlin:
// something to hold the stats...
data class SummaryStatisticsInt(var count: Int = 0,
var sum: Int = 0,
var min: Int = Int.MAX_VALUE,
var max: Int = Int.MIN_VALUE,
var avg: Double = 0.0) {
fun accumulate(newInt: Int): SummaryStatisticsInt {
count++
sum += newInt
min = min.coerceAtMost(newInt)
max = max.coerceAtLeast(newInt)
avg = sum.toDouble() / count
return this
}
}
// Now manually doing a fold, since Stream.collect is really just a fold
val stats = persons.fold(SummaryStatisticsInt()) { stats, person -> stats.accumulate(person.age) }
println(stats)
// output: SummaryStatisticsInt(count=4, sum=76, min=12, max=23, avg=19.0)
But it is better to create an extension function, 2 actually to match styles in Kotlin stdlib:
但是最好创建一个扩展函数,2 实际上是为了匹配 Kotlin stdlib 中的样式:
// Kotlin:
inline fun Collection<Int>.summarizingInt(): SummaryStatisticsInt
= this.fold(SummaryStatisticsInt()) { stats, num -> stats.accumulate(num) }
inline fun <T: Any> Collection<T>.summarizingInt(transform: (T)->Int): SummaryStatisticsInt =
this.fold(SummaryStatisticsInt()) { stats, item -> stats.accumulate(transform(item)) }
Now you have two ways to use the new summarizingInt
functions:
现在您有两种使用新summarizingInt
功能的方法:
val stats2 = persons.map { it.age }.summarizingInt()
// or
val stats3 = persons.summarizingInt { it.age }
And all of these produce the same results. We can also create this extension to work on Sequence
and for appropriate primitive types.
所有这些都会产生相同的结果。我们还可以创建此扩展来处理Sequence
和用于适当的原始类型。
For fun, compare the Java JDK code vs. Kotlin custom coderequired to implement this summarization.
为了好玩,比较实现此摘要所需的 Java JDK 代码与 Kotlin 自定义代码。
回答by Jayson Minard
There are some cases where it is hard to avoid calling collect(Collectors.toList())
or similar. In those cases, you can more quickly change to a Kotlin equivalent using extension functions such as:
在某些情况下,很难避免调用collect(Collectors.toList())
或类似的情况。在这些情况下,您可以使用扩展函数更快地更改为 Kotlin 等效项,例如:
fun <T: Any> Stream<T>.toList(): List<T> = this.collect(Collectors.toList<T>())
fun <T: Any> Stream<T>.asSequence(): Sequence<T> = this.iterator().asSequence()
Then you can simply stream.toList()
or stream.asSequence()
to move back into the Kotlin API. A case such as Files.list(path)
forces you into a Stream
when you may not want it, and these extensions can help you to shift back into the standard collections and Kotlin API.
然后您可以简单地stream.toList()
或stream.asSequence()
移回 Kotlin API。诸如此类的情况会在您可能不想要时Files.list(path)
强制您进入Stream
,而这些扩展可以帮助您重新使用标准集合和 Kotlin API。
回答by herman
More on laziness
更多关于懒惰
Let's take the example solution for "Compute sum of salaries by department" given by Jayson:
让我们以 Jayson 给出的“按部门计算工资总和”的示例解决方案为例:
val totalByDept = employees.groupBy { it.dept }.mapValues { it.value.sumBy { it.salary }}
In order to make this lazy (i.e. avoid creating an intermediate map in the groupBy
step), it is not possible to use asSequence()
. Instead, we must use groupingBy
and fold
operation:
为了使这个懒惰(即避免在groupBy
步骤中创建中间映射),不能使用asSequence()
. 相反,我们必须使用groupingBy
和fold
操作:
val totalByDept = employees.groupingBy { it.dept }.fold(0) { acc, e -> acc + e.salary }
To some people this may even be more readable, since you're not dealing with map entries: the it.value
part in the solution was confusing for me too at first.
对于某些人来说,这甚至可能更具可读性,因为您没有处理地图条目:it.value
解决方案中的部分起初也让我感到困惑。
Since this is a common case and we'd prefer not to write out the fold
each time, it may be better to just provide a generic sumBy
function on Grouping
:
由于这是一个常见的情况,我们不希望fold
每次都写出,最好只在 上提供一个通用sumBy
函数Grouping
:
public inline fun <T, K> Grouping<T, K>.sumBy(
selector: (T) -> Int
): Map<K, Int> =
fold(0) { acc, element -> acc + selector(element) }
so that we can simply write:
这样我们就可以简单地写:
val totalByDept = employees.groupingBy { it.dept }.sumBy { it.salary }