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