我们说我有这样的数据:
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))
我知道在我的数据中,没有Foo的实例具有相同的字段a
值 - 我想将其转换为Map[Int, Foo]
(我不想要{ {1}})
我可以:
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))
我的问题:
1)我怎样才能使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))
更加简洁/惯用?
2)我应该注意什么"在封面下#34;在上述两种解决方案中的每一种?即隐藏的内存/计算成本。特别是,我正在寻找一个" layperson""对breakOut
的解释并不一定涉及对breakOut
签名的深入讨论。
3)我是否应该注意其他任何解决方案(包括使用ScalaZ等库)?
答案 0 :(得分:5)
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]): That
CanBuildFrom
的目的是告诉scala如何从映射结果中生成That
(这是B
s的集合)。如果您不使用breakOut
,那么它会使用隐式CanBuildFrom
,但无论哪种方式,都必须有CanBuildFrom
,以便有一些对象能够构建That
} B
s。
最后,在您的breakOut
示例中,breakOut
完全是多余的,因为groupBy
生成Map
,.map
Map
Map
默认情况下,您会返回val m: Map[Int,Foo] = data.groupBy(_.a).map(e => e._1 -> e._2.head)
。
{{1}}