此答案描述了如何使用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)
视图解决了相同的问题,此外,又有访问元素:
val m = (List("A", "B", "C").view map (x => x -> x)).toMap
我假设View
包装器的创建相当便宜,所以我的问题是:是否有真正的理由在View
S?
breakOut
您将从英格兰到法国旅行。
带有视图:您正在笔记本和繁荣中的一组笔记,一旦您打电话给.force(),您就开始制作所有内容:buy a ticket, board on the plane, ....
突破性:您正在离开和繁荣时,您在巴黎看着埃菲尔铁塔。您不记得您到底是怎么到达那里的,但是实际上您没有留下任何回忆。
不好的类比,但我希望这可以让您对它们之间的区别有何不同。
我不认为views
和breakOut
是相同的。
breakOut
是CanBuildFrom
实现,用于通过消除中介步骤来简化变换操作。例如,没有中介集合从A到B。breakOut
意味着让Scala选择适当的构建器对象,以在给定情况下最大程度地生产新项目的效率。更多详细信息在这里。
views
处理不同类型的效率,主要的销售音调是:"不再有新对象"。视图存储对对象的灯光引用以应对不同的用法方案:懒惰访问等。
底线:
如果您在view
上的map
,则可能仍会在产生预期结果之前获得中介集合。您仍然可以从:
collection.view.map(somefn)(breakOut)
比:
collection.view.map(someFn)
从Scala 2.13开始,这不再是一个问题。突破已被删除,视图是建议的替换。
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)
Flavian所说的。
一个用例以保存内存。例如,如果您有一百万个字符长的字符串original
,并且需要一个一个一个字符串的所有后缀,则需要使用一个
val v = original.view
val suffixes = v.tails
在原始字符串上查看。然后,您可以使用suffix.force()
将它们转换回循环中的字符串,因此一次只能将其转换为一个,因此一次只能将一个固定在内存中。当然,您可以通过在原始字符串的索引上迭代迭代来做同样的事情,而不是创建任何类型的后缀集合。
另一个用例是,当派生对象的创建价格昂贵时,您需要在集合中(例如,作为地图中的值),但是您只会访问一些,而您不知道哪些。
如果您确实有一种有意义的选择,则选择 Breakout ,除非有一个很好的论点用于使用 view (如上所述)。
- 视图需要比 Breakout 更多的代码更改和护理,因为您需要在需要时添加 force()。根据上下文,不这样做是通常只在运行时检测到。使用突破,通常编译,是的。
- 在视图不适用的情况下, Breakout 由于视图生成和强迫被跳过,因此会更快。
- 如果您使用调试器,则可以检查收集内容,无法有意义地使用视图集合。