在Java 8中,有Stream.collect
允许集合上的聚合。在Kotlin中,这不是以同样的方式存在的,除了可能作为stdlib中的扩展函数的集合。但是不清楚不同用例的等价物是什么。
例如,top of the JavaDoc for Collectors
是为Java8编写的示例,当将它们移植到Kolin时,您无法在不同的JDK版本上使用Java8类,因此它们可能应该以不同的方式编写。
在线资源显示了Kotlin集合的例子,它们通常是微不足道的,不能真正与相同的用例进行比较。什么是真正符合Java 8 Stream.collect
文档中的用例的好例子?
- 将名称累积到列表中
- 将名称累积到树集中
- 将元素转换为字符串并将其串联,以逗号分隔
- 计算员工的工资总额
- 按部门对员工分组
- 按部门计算工资总额
- 把学生分为及格和不及格
详细信息请参见上面链接的JavaDoc。
***注:***此问题是作者(Self-Answered Questions)有意编写和回答的,以便SO中出现Kotlin常见主题的惯用答案。同时澄清一些为Kotlin α编写的非常古老的答案,这些答案对当前的Kotlin不准确。
4条答案
按热度按时间eiee3dmh1#
在某些情况下,很难避免调用
collect(Collectors.toList())
或类似的函数。在这些情况下,您可以使用扩展函数更快地更改为Kotlin等效函数,例如:然后您可以简单地使用
stream.toList()
或stream.asSequence()
重新使用KotlinAPI,例如Files.list(path)
会在您不需要时强制您使用Stream
,这些扩展可以帮助您重新使用标准集合和Kotlin API。ljsrvy3e2#
更多关于懒惰
我们以Jayson给出的“按部门计算工资总额”为例:
为了使这个操作变得懒惰(即避免在
groupBy
步骤中创建中间Map),不可能使用asSequence()
,而是必须使用groupingBy
和fold
操作:对于某些人来说,这可能更容易理解,因为您不需要处理Map条目:解决方案中的
it.value
部分一开始也让我感到困惑。由于这是一种常见的情况,我们不希望每次都写出
fold
,因此最好在Grouping
上提供一个通用的sumBy
函数:所以我们可以简单地写:
lg40wkob3#
Kotlin stdlib中有一些函数可以用于平均、计数、非重复、过滤、查找、分组、连接、Map、最小值、最大值、分区、切片、排序、求和、到/从数组、到/从列表、到/从Map、联合、协同迭代、所有函数范例等等。因此,您可以使用这些函数创建小的1-liner,而无需使用Java 8中更复杂的语法。
Collectors
类唯一缺少的是总结(但在another answer to this question中是一个简单的解决方案)*。两者都缺少一点是按计数进行批处理,这在another Stack Overflow answer中可以看到,并且答案也很简单。另一个有趣的例子也来自Stack Overflow:Idiomatic way to split sequence into three lists using Kotlin。如果您想创建类似
Stream.collect
的内容用于其他目的,请参见Custom Stream.collect in Kotlin在创建可能已经存在于API Reference for kotlin.collections中的新函数之前,将其作为一个整体进行探索总是很好的。
下面是从Java 8
Stream.collect
示例到Kotlin中等效示例的一些转换:一个二个一个一个
一个12b1x一个13b1x
一个16b1x一个17b1x
一个18块一个19块一个
在所有情况下,不需要特殊的折叠、减少或其他功能来模拟
Stream.collect
。如果您有进一步的用例,请在注解中添加它们,我们就可以看到了!关于懒惰
如果你想延迟处理一个链,你可以在链之前使用
asSequence()
转换成Sequence
,在函数链的末尾,你通常也会得到一个Sequence
,然后你可以使用toList()
,toSet()
,toMap()
或者其他函数来具体化末尾的Sequence
。为什么没有类型?!?
你会注意到Kotlin的例子没有指定类型。这是因为Kotlin有完整的类型推断,并且在编译时是完全类型安全的。比Java更安全,因为它也有可空类型,可以帮助防止可怕的NPE。所以在Kotlin中:
等同于:
由于Kotlin知道
people
是什么,并且people.age
是Int
,因此过滤器表达式仅允许与Int
进行比较,并且people.name
是String
,因此map
步骤产生List<String>
(String
的只读List
)。现在,如果
people
可能是null
,就像在List<People>?
中一样,那么:返回需要进行空值检查的
List<String>?
(* 或使用其他Kotlin运算符之一检查可空值,请参阅Kotlin idiomatic way to deal with nullable values和Idiomatic way of handling nullable or empty list in Kotlin *)另请参见:
4dbbbstv4#
下面是从Java 8 Stream Tutorial转换到Kotlin的所有示例,每个示例的标题都来自源文章:
一个二个一个一个
或者,在String上创建一个名为ifPresent的扩展函数:
另请参阅:
apply()
function另请参阅:Extension Functions
另请参见:
?.
安全调用运算符,以及一般可空性:在Kotlin中,处理可空值、引用或转换它们的惯用方法是什么一个一个一个一个
一个一个三个一个一个
为什么顺序很重要
Java 8流教程的这一部分对于Kotlin和Java是相同的。
重用流
在Kotlin中,它取决于集合的类型是否可以被多次使用。
Sequence
每次都会生成一个新的迭代器,除非它声明"只使用一次",否则它可以在每次被操作时重置到开始。因此,虽然下面的代码在Java 8 stream中失败,但在Kotlin中可以工作:一个17块一个18块一个
在Java中得到同样的行为:
因此在Kotlin中,数据提供者决定它是否可以重置并提供新的迭代器,但是如果你想有意地将
Sequence
限制在一次迭代中,你可以使用constrainOnce()
函数来实现Sequence
:高级操作
一个21个1x一个22个1x
顺便说一句,在Kotlin中,我们可以创建简单的data classes并示例化测试数据,如下所示:
好的,这里有一个Kotlin更有趣的案例。首先是探索从集合/序列创建
Map
的变体的错误答案:现在是正确答案:
我们只需要连接匹配的值来折叠列表,并为
jointToString
提供一个转换器,以便从Person
示例移动到Person.name
。好的,这个问题不需要定制
Collector
就可以很容易地完成,所以让我们用Kotlin的方法来解决它,然后设计一个新的示例,展示如何对Kotlin中本机不存在的Collector.summarizingInt
执行类似的过程。一个27块一个一个28块一个
summarizingInt
方法和一个匹配的示例:但最好创建一个扩展函数,2实际上是为了匹配Kotlin stdlib中的样式:
现在有两种方法可以使用新的
summarizingInt
函数:所有这些都产生相同的结果,我们也可以创建这个扩展来处理
Sequence
和适当的原语类型。为了好玩,compare the Java JDK code vs. Kotlin custom code需要实现这个总结。