假设我有这样的数据:
scala> case class Foo(a: Int, b: Int)
defined class Foo
scala> val data: List[Foo] = Foo(1,10) :: Foo(2, 20) :: Foo(3,30) :: Nil
data: List[Foo] = List(Foo(1,10), Foo(2,20), Foo(3,30))我知道,在我的数据中,不存在具有相同值的字段a的Foo实例--我希望将其转换为Map[Int, Foo] (我不想要Map[Int, List[Foo]])
我可以:
scala> val m: Map[Int,Foo] = data.groupBy(_.a).mapValues(_.head)
m: Map[Int,Foo] = Map(2 -> Foo(2,20), 1 -> Foo(1,10), 3 -> Foo(3,30))或者:
scala> val m: Map[Int,Foo] = data.groupBy(_.a).map(e => e._1 -> e._2.head)(collection.breakOut)
m: Map[Int,Foo] = Map(2 -> Foo(2,20), 1 -> Foo(1,10), 3 -> Foo(3,30))我的问题:
1)如何使使用breakOut的实现更加简洁/惯用?
2)在上述两种解决方案中,我都应该注意到“幕后”是什么?即隐藏内存/计算成本。特别是,我正在寻找一个“外行人”对breakOut的解释,它不一定涉及到对map签名的深入讨论。
3)是否还有其他我应该知道的解决方案(例如,包括使用诸如ScalaZ之类的库)?
发布于 2014-07-05 17:55:18
1)正如@Kigyo指出的那样,如果没有重复的a,正确的答案是不会使用groupBy的
val m: Map[Int,Foo] = data.map(e => e.a -> e)(breakOut)当可能存在重复的groupBy时,使用a是很好的,但是考虑到您的问题,完全没有必要。
2)首先,如果计划多次访问值,则不要使用mapValues。.mapValues方法不创建新的Map (就像.map方法一样)。相反,它创建一个Map视图,该视图在每次访问函数时重新计算函数(在您的情况下是_.head)。如果您计划访问很多东西,那么请考虑使用map{case (a,b) => a -> ??}。
其次,将breakOut函数作为CanBuildFrom参数传递不会增加成本。原因是CanBuildFrom参数总是存在,只是有时它是隐式的。真正的特征是:
def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): ThatCanBuildFrom的目的是告诉scala如何利用映射的结果(它是B的集合)生成That。如果您不使用breakOut,那么它将使用隐式CanBuildFrom,但是无论哪种方式,都必须有一个CanBuildFrom,以便有一些对象能够从B构建That。
最后,在使用breakOut的示例中,breakOut是完全冗余的,因为groupBy生成Map,因此Map上的.map默认返回Map。
val m: Map[Int,Foo] = data.groupBy(_.a).map(e => e._1 -> e._2.head)https://stackoverflow.com/questions/24588674
复制相似问题