问题描述
在 Java 8 中,有 Stream.collect
允许对集合进行聚合.在 Kotlin 中,这不以相同的方式存在,除了可能作为 stdlib 中的扩展函数集合.但尚不清楚不同用例的等效项是什么.
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.
例如,在 顶部Collectors
的 JavaDoc 是为 Java 8 编写的示例,当将它们移植到 Kolin 时,您不能在不同的 JDK 版本上使用 Java 8 类,因此它们可能应该以不同的方式编写.
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.
就展示 Kotlin 集合示例的在线资源而言,它们通常是微不足道的,并不能真正与相同的用例进行比较.什么是真正符合 Java 8 Stream.collect
记录的案例的好例子?清单是:
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:
- 将名称累积到列表中
- 将名称累积到 TreeSet 中
- 将元素转换为字符串并将它们连接起来,用逗号分隔
- 计算员工工资总额
- 按部门对员工进行分组
- 按部门计算工资总额
- 将学生分为合格和不合格
在上面链接的 JavaDoc 中有详细信息.
With details in the JavaDoc linked above.
One thing missing from both is batching by count, which is seen in another Stack Overflow answer and 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
编辑 11.08.2017: 在 kotlin 1.2 M2 中添加了分块/窗口化集合操作,请参阅 https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
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/
探索 kotlin 的 API 参考总是好的.在创建可能已经存在的新函数之前,将集合作为一个整体.
It is always good to explore the API Reference for kotlin.collections as a whole before creating new functions that might already exist there.
以下是一些从 Java 8 Stream.collect
示例到 Kotlin 中等效项的转换:
Here are some conversions from Java 8 Stream.collect
examples to the equivalent in Kotlin:
将名称累积到列表中
// Java:
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
// Kotlin:
val list = people.map { it.name } // toList() not needed
将元素转换为字符串并将它们连接起来,用逗号分隔
// Java:
String joined = things.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
// Kotlin:
val joined = things.joinToString(", ")
计算员工工资总额
// Java:
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val total = employees.sumBy { it.salary }
按部门对员工进行分组
// Java:
Map<Department, List<Employee>> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// Kotlin:
val byDept = employees.groupBy { it.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 }}
将学生分为合格和不合格
// 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 }
男性成员姓名
// 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 }
花名册中成员的性别分组名称
// 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 } }
过滤一个列表到另一个列表
// Java:
List<String> filtered = items.stream()
.filter( item -> item.startsWith("o") )
.collect(Collectors.toList());
// Kotlin:
val filtered = items.filter { it.startsWith('o') }
寻找最短的字符串列表
// Java:
String shortest = items.stream()
.min(Comparator.comparing(item -> item.length()))
.get();
// Kotlin:
val shortest = items.minBy { it.length }
应用过滤器后计算列表中的项目
// 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') }
然后继续......在所有情况下,都不需要特殊的折叠、减少或其他功能来模仿 Stream.collect
.如果您有更多用例,请在评论中添加它们,我们可以看到!
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!
如果你想延迟处理一个链,你可以在链之前使用 asSequence()
转换为 Sequence
.在函数链的末尾,您通常还会得到一个 Sequence
.然后你可以使用toList()
、toSet()
、toMap()
或其他一些函数来具体化Sequence
最后.
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.
// 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 中:
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:
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.age
是 Int
因此过滤器表达式只允许与 进行比较Int
,并且 people.name
是一个 String
因此 map
步骤产生一个 List
code>(只读List
of String
).
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
可能是 null
,就像在 List?
中那样:
Now, if people
were possibly null
, as-in a List<People>?
then:
val someList = people?.filter { it.age <= 30 }?.map { it.name }
返回一个 List?
需要检查空值(或使用其他 Kotlin 运算符之一获取可空值,请参阅此 Kotlin 处理可空值的惯用方法以及 在 Kotlin 中处理可空列表或空列表的惯用方式)
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 values and also Idiomatic way of handling nullable or empty list in Kotlin)
这篇关于标准 Kotlin 库中有哪些 Java 8 Stream.collect 等效项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!