Kotlin stdlib operatios vs for循环

roz*_*ina 15 kotlin

我写了以下代码:

val src = (0 until 1000000).toList()
val dest = ArrayList<Double>(src.size / 2 + 1)    

for (i in src)
{
    if (i % 2 == 0) dest.add(Math.sqrt(i.toDouble()))
}
Run Code Online (Sandbox Code Playgroud)

IntellJ(在我的例子中是AndroidStudio)问我是否要用stdlib中的操作替换for循环.这导致以下代码:

val src = (0 until 1000000).toList()
val dest = ArrayList<Double>(src.size / 2 + 1)
src.filter { it % 2 == 0 }
   .mapTo(dest) { Math.sqrt(it.toDouble()) }
Run Code Online (Sandbox Code Playgroud)

现在我必须说,我喜欢改变的代码.当我提出类似的情况时,我发现写入比循环更容易.但是在阅读了filter函数的功能后,我意识到与for循环相比,这是一个很慢的代码.filterfunction创建一个新列表,其中仅包含src中与谓词匹配的元素.因此,在stdlib版本的代码中创建了另外一个列表和一个循环.对于小型列表来说,它可能并不重要,但总的来说这听起来不是一个好的选择.特别是如果要连接更多这样的方法,你可以通过编写for循环来获得许多可以避免的额外循环.

我的问题是什么被认为是Kotlin的良好做法.我应该坚持使用循环还是我错过了一些东西而且它不起作用,因为我觉得它有效.

gle*_*e8e 14

如果您担心性能,那么您需要的是Sequence.例如,您的上述代码将是

val src = (0 until 1000000).toList()
val dest = ArrayList<Double>(src.size / 2 + 1)
src.asSequence()
    .filter { it % 2 == 0 }
    .mapTo(dest) { Math.sqrt(it.toDouble()) }
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,filter返回另一个Sequence代表中间步骤的代码.什么都没有真正创建,没有对象或数组创建(除了新的Sequence包装器).只有在mapTo调用终端操作符时才会创建生成的集合.

如果你已经学过java 8流,你可能会发现上面的解释有些熟悉.实际上,Sequence大致相当于java 8 Stream的kotlin.它们具有相似的目的和性能特征.唯一的区别是Sequence不适合使用ForkJoinPool,因此更容易实现.

当涉及多个步骤或集合可能很大时,建议使用Sequence而不是普通.filter {...}.mapTo{...}.我还建议您使用Sequence表单而不是命令式表单,因为它更容易理解.当数据处理涉及5个或更多步骤时,命令形式可能变得复杂,因此难以理解.如果只有一个步骤,则不需要a Sequence,因为它只会创建垃圾并且没有任何用处.

  • 有时几个循环.即使他们复制了整个系列,也因为良好的参考位置而表现出良好的性能.请参阅:/sf/ask/2494041161/ (2认同)
  • @rozina [这篇SO帖子](/sf/answers/35928161/)解释了如何在JVM上正确的基准测试.虽然它的目的是教育Java程序员,但它也适用于kotlin,因为kotlin具有JVM后端.以下是我的测试代码的要点:https://gist.github.com/Glease/9​​00fe08d757631e97e230d90a9b4faa2 (2认同)