假设我有这样的数据:
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]): That
CanBuildFrom
的目的是告诉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
复制相似问题