Scala groupBy + mapValues vs. groupBy + map + breakOut

时间:2014-07-05 16:59:51

标签: scala

我们说我有这样的数据:

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等库)?

1 个答案:

答案 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}}