【发布时间】:2018-12-09 11:32:51
【问题描述】:
阅读Programming in Scala, 3rd Edition时,它说
类列表确实提供了“追加”操作——它是这样写的:+ 但是这个 操作很少使用,因为附加到一个 列表随着列表的大小线性增长,而在前面加上 :: 需要恒定的时间。
如果你想通过附加元素有效地构建一个列表,你可以 把它们放在前面,当你完成后调用 reverse。
我试图理解,Scala 的惯用方式是什么?与 ListBuffer 相比,两次调用 List.reverse 是否可接受且高效(因为 ListBuffer 是可变的)?
// Scala Recommended way - but reverse twice?
val alist = List("A", "B")
// cons is O(1)
// This will print (A, B, C)
println(("C" :: alist.reverse).reverse)
// Scala also Recommended: Use ListBuffer
val alb = ListBuffer("A", "B")
alb.append("C")
val clist2 = alb.toList
// This will print (A, B, C)
println(clist2)
// DO NOT do this, its O(n)
val clist3 = alist :+ "C"
// This will print (A, B, C)
println(clist3)
P.S:我在这里不是指代码优化。一般推荐哪一种,不会收到WTH的表达。
【问题讨论】:
-
8 个月前我从 java 搬到了 scala。我在前 6 个月编写的代码加载了可变集合。现在我从不使用它们并且非常关心性能。一旦你运行了真正的大数据,你就会自己明白为什么。这是一个自我发现的项目。现在,相信这些文件。 :)
-
我对您在第三个代码 sn-p 上方的评论感到有些困惑。 “不要这样做,它的 O(n)”是什么意思?所有三个 sn-ps 都是 O(n)。
List.reverse是 O(n),ListBuffer.toList是 O(n),List.:+是 O(n)。 -
@JörgWMittag 我认为
ListBuffer.toList实际上是恒定时间(除非您在将 ListBuffer 转换为List 后重新使用它) -
@joelb:你是对的。有趣的。因此,在调用 toList 之后改变缓冲区会延迟触发 O(n) 复制,但原始调用是 O(1)。聪明。
-
这就是所有问题的答案。 docs.scala-lang.org/overviews/collections/…