假设我有这样的数据:
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))
Run Code Online (Sandbox Code Playgroud)
我知道在我的数据中,不会有Foo实例具有相同的field值a-我想将其转换为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))
Run Code Online (Sandbox Code Playgroud)
要么:
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))
Run Code Online (Sandbox Code Playgroud)
我的问题:
1)我怎样才能使实现breakOut更加简洁/惯用?
2)在以上两种解决方案的每一种中,我应该了解什么?即隐藏的内存/计算成本。特别是,我正在寻找对此的“外行人士”解释breakOut,并不一定涉及对的签名的深入讨论map。
3)还有其他我应该知道的解决方案(例如,使用诸如ScalaZ之类的库)吗?
1)正如@Kigyo所指出的,鉴于没有重复a的,正确的答案将不使用groupBy:
val m: Map[Int,Foo] = data.map(e => e.a -> e)(breakOut)
Run Code Online (Sandbox Code Playgroud)
groupBy当可能有重复的as时,使用很好,但是鉴于您的问题,则完全没有必要。
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
Run Code Online (Sandbox Code Playgroud)
的目的CanBuildFrom是告诉scala如何That从映射结果(是的集合B)中得出结果。如果breakOut不使用,则它使用隐式CanBuildFrom,但无论哪种方式,都必须存在,CanBuildFrom以便某些对象能够That根据Bs 进行构建。
最后,在带有的示例中breakOut,breakOut由于groupBy产生Map,所以完全是多余的,因此默认情况下,.mapon Map会给您返回a Map。
val m: Map[Int,Foo] = data.groupBy(_.a).map(e => e._1 -> e._2.head)
Run Code Online (Sandbox Code Playgroud)