在 Java 8 中,Stream.collect允许对集合进行聚合。在 Kotlin 中,这并不以相同的方式存在,除了可能作为 stdlib 中的扩展函数集合之外。但目前尚不清楚不同用例的等价物是什么。
Stream.collect
例如,JavaDoc 的顶部是Collectors为 Java 8 编写的示例,当将它们移植到 Kolin 时,在不同的 JDK 版本上您不能使用 Java 8 类,因此很可能它们应该以不同的方式编写。
Collectors
就显示 Kotlin 集合示例的在线资源而言,它们通常是微不足道的,并且不能真正与相同的用例进行比较。什么是真正符合 Java 8 文档的案例的好例子Stream.collect?那里的清单是:
上面链接的 JavaDoc 中有详细信息。
注意: 这个问题是作者有意编写和回答的( Self-Answered Questions),因此常见的 Kotlin 主题的惯用答案出现在 SO 中。还要澄清一些为 Kotlin 的 alpha 编写的非常古老的答案,这些答案对于当今的 Kotlin 并不准确。
Kotlin 标准库中有用于平均、计数、不同、过滤、查找、分组、连接、映射、最小值、最大值、分区、切片、排序、求和、到/从数组、到/从列表、到/从映射的函数,联合,共同迭代,所有功能范式等等。因此,您可以使用它们来创建小的 1-liner,而无需使用更复杂的 Java 8 语法。
两者都缺少的一件事是按计数进行批处理,这在另一个答案中可以看到,并且也有一个简单的答案。另一个有趣的案例也是来自:使用 Kotlin 将序列溢出到三个列表中的惯用方式。如果您想Stream.collect为其他目的创建类似的东西,请参阅Kotlin 中的 Custom Stream.collect
在创建可能已经存在的新函数之前,最好先探索kotlin.collections 的 API 参考。
Stream.collect以下是从 Java 8示例到 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. 如果您有更多用例,请在评论中添加它们,我们可以看到!
如果你想惰性处理一个链,你可以在链前转换为Sequenceusing asSequence()。在函数链的末尾,通常Sequence也会以 a 结尾。然后您可以使用toList(),toSet()或toMap()其他一些函数来实现Sequence最后的 。
Sequence
asSequence()
toList()
toSet()
toMap()
// 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,因此过滤器表达式只允许与 a 进行比较,而那people.age是a ,因此该步骤产生 a (只读)。Int``Int``people.name``String``map``List<String>``List``String
people
people.age
Int``Int``people.name``String``map``List<String>``List``String
现在,如果people可能的话null,就像一个List<People>?then:
null
List<People>?
val someList = people?.filter { it.age <= 30 }?.map { it.name }
返回List<String>?需要进行空值检查的 a ( _或使用其他 Kotlin 运算符之一来处理可空值
List<String>?