在 Java 8 中,有 Stream.collect
允许对集合进行聚合。在 Kotlin 中,这并不以相同的方式存在,除了可能作为 stdlib 中的扩展函数集合之外。但目前尚不清楚不同用例的等价物是什么。
例如,在 top of the JavaDoc for Collectors
是为 Java 8 编写的示例,当将它们移植到 Kolin 时,您不能在不同的 JDK 版本上使用 Java 8 类,因此很可能它们应该以不同的方式编写。
就显示 Kotlin 集合示例的在线资源而言,它们通常是微不足道的,并不能真正与相同的用例进行比较。什么是真正符合 Java 8 Stream.collect
文档的案例的好例子?列表如下:
- 将名称累积到列表中
- 将名称累积到 TreeSet 中
- 将元素转换为字符串并将它们连接起来,用逗号分隔
- 计算员工工资总额
- 按部门分组员工
- 按部门计算工资总和
- 将学生分为合格和不合格
上面链接的 JavaDoc 中有详细信息。
注意: 这个问题是作者有意编写和回答的 (Self-Answered Questions),因此出现了对 Kotlin 常见问题的惯用答案在所以。还要澄清一些为 Kotlin alpha 编写的非常古老的答案,这些答案对于当前的 Kotlin 并不准确。
最佳答案
在 Kotlin 标准库中有用于平均、计数、不同、过滤、查找、分组、连接、映射、最小值、最大值、分区、切片、排序、求和、到/从数组、到/从列表、到/from maps、union、co-iteration、所有功能范式等等。因此,您可以使用它们来创建小的 1-liner,而无需使用 Java 8 更复杂的语法。
我认为内置 Java 8 中唯一缺少的东西 Collectors
类是总结(但在 another answer to this question 中是一个简单的解决方案)。
两者都缺少一个按计数进行批处理,见 another Stack Overflow answer并且也有一个简单的答案。另一个有趣的案例也是来自 Stack Overflow 的案例:Idiomatic way to spilt sequence into three lists using Kotlin .如果你想创建类似 Stream.collect
的东西出于其他目的,请参阅 Custom Stream.collect in Kotlin
EDIT 11.08.2017: 在 kotlin 1.2 M2 中添加了分 block /窗口收集操作,请参阅 https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
探索 API Reference for kotlin.collections 总是好的。在创建可能已经存在的新函数之前作为一个整体。
以下是 Java 8 的一些转换 Stream.collect
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
.如果您还有其他用例,请在评论中添加它们,我们可以看到!
关于懒惰
如果你想惰性处理一个链,你可以转换成 Sequence
使用 asSequence()
在链条之前。在函数链的末尾,您通常会得到 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.age
是 Int
因此过滤器表达式只允许比较 Int
,以及 people.name
是 String
因此 map
步骤产生 List<String>
(只读 List
的 String
)。
现在,如果 people
可能是 null
, 如 List<People>?
那么:
val someList = people?.filter { it.age <= 30 }?.map { it.name }
返回 List<String>?
需要进行空值检查(或使用其他 Kotlin 运算符之一来获取可空值,参见 Kotlin idiomatic way to deal with nullable values 和 Idiomatic way of handling nullable or empty list in Kotlin )
另见:
- extension functions for Iterable 的 API 引用
- API 引用 extension functions for Array
- API 引用 extension functions for List
- API 引用 extension functions to Map
关于java - 标准 Kotlin 库中有哪些 Java 8 Stream.collect 等效项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34642254/