scala.collection.breakOut vs views

问题描述 投票:14回答:4

This SO answer描述了如何使用scala.collection.breakOut来防止产生浪费的中间集合。例如,在这里我们创建一个中间Seq[(String,String)]

val m = List("A", "B", "C").map(x => x -> x).toMap

通过使用breakOut,我们可以阻止创建这个中间Seq

val m: Map[String,String] = List("A", "B", "C").map(x => x -> x)(breakOut)

Views solve the same problem以及懒惰的访问元素:

val m = (List("A", "B", "C").view map (x => x -> x)).toMap

我假设View包装的创建相当便宜,所以我的问题是:有没有任何真正的理由使用breakOut而不是Views?

scala scala-collections
4个回答
14
投票

你打算从英国到法国旅行。

有了观点:你在你的笔记本电脑和热潮中拿了一套笔记,一旦你打电话给.force(),你就开始制作所有笔记:buy a ticket, board on the plane, ....

随着breakOut:你正在离开和繁荣,你在巴黎看着埃菲尔铁塔。你不记得你到底是怎么到达那里的,但实际上你做过这次旅行,只是没有回忆。

不好比喻,但我希望这能让你体会到它们之间的区别。


12
投票

我不认为viewsbreakOut是相同的。

breakOut是一个CanBuildFrom实现,用于通过消除中间步骤来简化转换操作。例如,在没有中间收集的情况下从A到B. breakOut意味着让Scala选择适当的构建器对象,以便在给定方案中最大限度地提高生成新项目的效率。更多细节here

views处理不同类型的效率,主要的销售宣传是:“没有更多的新对象”。视图存储对对象的灯光引用以处理不同的使用场景:延迟访问等。

底线:

如果你在map上使用view,你仍然可以获得在产生预期结果之前创建的中间参考集合。您仍然可以从以下方面获得卓越的性

collection.view.map(somefn)(breakOut)

比起:

collection.view.map(someFn)

1
投票

什么黄油说。

视图的一个用例是节省内存。例如,如果你有一个百万字符长的字符串original,并且需要逐个使用该字符串的所有百万个后缀,你可以使用一个集合

val v = original.view
val suffixes = v.tails

查看原始字符串。然后你可以逐个遍历后缀,使用suffix.force()将它们转换回循环中的字符串,因此一次只能在内存中保存一个。当然,你可以通过在原始字符串的索引上迭代自己的循环来做同样的事情,而不是创建任何类型的后缀集合。

另一个用例是当派生对象的创建成本很高时,你需要它们在一个集合中(比如,作为地图中的值),但你只会访问一些,而你不知道哪些。

如果你真的有一个案例,它们之间的选择是有意义的,更喜欢breakOut,除非有一个很好的论据使用视图(如上所述)。

  • 视图需要比breakOut更多的代码更改和关注,因为您需要在需要的地方添加force()。根据上下文,通常仅在运行时检测到失败。使用breakOut,通常如果它编译,它是正确的。
  • 如果视图不适用,则breakOut会更快,因为会跳过视图生成和强制。
  • 如果使用调试器,则可以检查集合内容,这些内容对于一组视图无意义。

1
投票

从Scala 2.13开始,这不再是一个问题。已删除突破,建议替换视图。


Scala 2.13 Collections Rework

视图也是collection.breakOut的推荐替代品。例如,

val s: Seq[Int] = ... 
val set: Set[String] = s.map(_.toString)(collection.breakOut)

可以用与以下相同的性能特征表达:

val s: Seq[Int] = ... 
val set = s.view.map(_.toString).to(Set)
© www.soinside.com 2019 - 2024. All rights reserved.