什么Java 8 Stream.collect等价物在标准的Kotlin库中可用? [英] What Java 8 Stream.collect equivalents are available in the standard Kotlin library?
问题描述
在Java 8中,有 Stream.collect
允许集合上的聚合。在Kotlin中,这不以同样的方式存在,除了可能作为stdlib中的扩展函数的集合。但是不清楚不同用例的等效性。
例如,在 Collectors
的JavaDoc顶部是为Java 8编写的示例,当将它们移植到Kolin时,不能使用Java 8类在不同的JDK版本上,所以可能应该写成不同的。
在资源在线显示Kotlin集合的例子,他们通常是微不足道的,并不真正比较相同的用例。什么是很好的例子,真正匹配的案例,如记录为Java 8 Stream.collect
?列表中有:
- 将名称累积到列表中
- 将名称累积到TreeSet
-
- 将元素转换为字符串并用逗号分隔
- 计算员工的工资总额
- 按部门计算工资总额
- 将学生分成通过和失败
有关上述链接的JavaDoc的详细信息。
注意: >这个问题是由作者故意写和回答的(自我回答的问题),以便常用的Kotlin主题的惯用答案出现在SO中。还要澄清一些为Kotlin的alphas写的,对当前Kotlin不准确的老的答案。
Kotlin stdlib中有用于平均,计数,不同,过滤,查找,分组,连接,映射,最小, ,切片,排序,求和,到/从数组,到/从列表,到/从映射,联合,协同,所有的功能范例等等。所以你可以使用它们来创建一个简单的1-liners,并且没有必要使用更复杂的Java 8语法。
我认为唯一缺少的来自内置的Java 8 Collectors
类是摘要(但在另一个答案这个问题是一个简单的解决方案)。
两者之间缺少的一件事是按计数进行批量处理,这在另一个Stack Overflow answer ,并有一个简单的答案。另一个有趣的情况是这也是从Stack Overflow:惯用语使用Kotlin将溢出序列分成三个列表。如果您想要创建 Stream.collect
以实现其他目的,请参阅自定义流。收集在Kotlin
总是好的探索用于kotlin.collections的API参考作为一个整体,然后创建可能已经存在的新函数。
这里是一些从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()不需要
并连接起来,并以逗号分隔
// Java:
String join = things.stream()
.map(Object :: toString)
.collect(Collectors.joining(,));
// Kotlin:
val joined = things .joinToString(,)
计算员工工资总额 / p>
// Java:
int total = employees.stream()
.collect(Collectors.summingInt(Employee :: getSalary)));
// Kotlin:
val total = .sumBy {it.salary}
按部门分组员工
// Java:
Map< Department,List< Employee> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee :: getDepartment));
// Kotlin:
val byDept = employees .groupBy {it.department}
按部门计算工资总和 / p>
// 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>> passedFailing =
students.stream()
.collect(Collectors.partitioningBy(s - > s.getGrade()> = PASS_THRESHOLD));
// Kotlin:
val passingFailing = students .partition {it.grade> = PASS_THRESHOLD}
男性成员姓名
// Java:
List< String> namesOfMaleMembersCollect = 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}
按性别列在成员名单中的成员名称
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 {item.startsWith('o')}
/ strong>
// Java:
String shortest = items.stream
.min(Comparator.comparing(item - > item.length()))
.get();
// Kotlin:
val short = items .minBy {it.length}
在应用过滤器后对列表中的项目进行计数
// Java:
long count = items.stream filter(item - > item.startsWith(t))。count();
// Kotlin:
val count = items .filter {it.startsWith('t')} .size
//但最好不要过滤,但是使用谓词
val count = items.count {it.startsWith('t') }
并在它上...在所有情况下,没有特殊的折叠,缩小或其他功能需要模仿 Stream.collect
。如果您有其他用例,请在评论中添加它们,我们可以看到!
关于懒惰
想要延迟处理链,可以在链之前使用 asSequence()
转换为序列
。在函数链的末尾,通常最后还有一个 Sequence
。然后可以使用 toList()
, toSet()
, toMap c $ c>或一些其他函数来实现
Sequence
结束。
//切换到和从lazy
val someList = items.asSequence()。filter {...} .take(10).map {...} .toList()
//切换到lazy,但sorted()在最后再次出现
val someList = items.asSequence()。filter {...} .take(10).map {... } .sorted()
为什么没有Types?!?
你会注意到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.age
是 Int
因此过滤器表达式只允许与 Int
进行比较, people.name
是 String
,因此 map
步骤产生一个 List< String>
(readonly <$现在,如果<$ c>
null
,因为在列表<人>?
那么: val someList = people?.filter {it.age< = 30}?map {it.name}
返回 List< String>?
将需要为null选中(或使用其他Kotlin运算符作为可空值,请参阅此 Kotlin处理可空值的常用方法以及在Kotlin中处理可空或空列表的惯用方法 )
另请参阅:
- API参考 Iterable的扩展函数
- Array 的扩展函数
- 扩展功能的API参考列表
- 扩展功能到地图
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.
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.
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:
- 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
With details in the JavaDoc linked above.
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.
These examples and more, are now maintained in Stack Overflow Documentation
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.
I think the only thing missing from the built-in Java 8 Collectors
class is summarization (but in another answer to this question is a simple solution).
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
It is always good to explore the API Reference for kotlin.collections as a whole before creating new functions that might already exist there.
Here are some conversions from Java 8 Stream.collect
examples to the equivalent in 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> namesOfMaleMembersCollect = 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 { item.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!
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.
// 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:
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
).
Now, if people
were possibly null
, as-in a List<People>?
then:
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 values and also Idiomatic way of handling nullable or empty list in 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
这篇关于什么Java 8 Stream.collect等价物在标准的Kotlin库中可用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!