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
而不是View
s?
你打算从英国到法国旅行。
有了观点:你在你的笔记本电脑和热潮中拿了一套笔记,一旦你打电话给.force(),你就开始制作所有笔记:buy a ticket, board on the plane, ....
随着breakOut:你正在离开和繁荣,你在巴黎看着埃菲尔铁塔。你不记得你到底是怎么到达那里的,但实际上你做过这次旅行,只是没有回忆。
不好比喻,但我希望这能让你体会到它们之间的区别。
我不认为views
和breakOut
是相同的。
breakOut
是一个CanBuildFrom
实现,用于通过消除中间步骤来简化转换操作。例如,在没有中间收集的情况下从A到B. breakOut
意味着让Scala选择适当的构建器对象,以便在给定方案中最大限度地提高生成新项目的效率。更多细节here。
views
处理不同类型的效率,主要的销售宣传是:“没有更多的新对象”。视图存储对对象的灯光引用以处理不同的使用场景:延迟访问等。
底线:
如果你在map
上使用view
,你仍然可以获得在产生预期结果之前创建的中间参考集合。您仍然可以从以下方面获得卓越的性
collection.view.map(somefn)(breakOut)
比起:
collection.view.map(someFn)
什么黄油说。
视图的一个用例是节省内存。例如,如果你有一个百万字符长的字符串original
,并且需要逐个使用该字符串的所有百万个后缀,你可以使用一个集合
val v = original.view
val suffixes = v.tails
查看原始字符串。然后你可以逐个遍历后缀,使用suffix.force()
将它们转换回循环中的字符串,因此一次只能在内存中保存一个。当然,你可以通过在原始字符串的索引上迭代自己的循环来做同样的事情,而不是创建任何类型的后缀集合。
另一个用例是当派生对象的创建成本很高时,你需要它们在一个集合中(比如,作为地图中的值),但你只会访问一些,而你不知道哪些。
如果你真的有一个案例,它们之间的选择是有意义的,更喜欢breakOut,除非有一个很好的论据使用视图(如上所述)。
从Scala 2.13开始,这不再是一个问题。已删除突破,建议替换视图。
视图也是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)