我需要创建一个新对象来混入Scala trait吗?

6
在Scala中,对集合调用groupBy()会返回一个值为集合的Map,但我需要一个MultiMap。进行转换的最简单方法是什么?是否可以避免创建新的MultiMap并将所有内容复制过去?
1个回答

5
我认为对于“我是否需要创建一个新对象来混合Scala trait?”的问题,答案是“是”。您可以通过包装对象和隐式转换来最小化一些痛苦。
针对您的具体问题,我无法强制groupBy(...)返回可变映射到可变集合,您需要使用“MapProxy with MultiMap”进行包装。但是,实现自己版本的“groupBy”并不需要太多的代码:
package blevins.example

object App extends Application {

  implicit def multiMapable[B](c: Iterable[B]) = new {
    def groupByMM[A](f: B => A) = {
      import scala.collection.mutable._
      val ret = new HashMap[A,Set[B]] with MultiMap[A,B]
      for (e <- c) { ret.addBinding(f(e), e) }
      ret
    } 
  }

  val c = List(1,2,3,4,5,6,7,8,9)
  val mm = c.groupByMM { i => if (i < 5) "alpha" else "beta" }
  mm.addBinding("alpha",12)
  println(mm) // Map(beta -> Set(5, 7, 6, 9, 8), alpha -> Set(3, 1, 4, 2, 12))

}

补充说明

以下是将已有的Map[String,Set[Int]]转换成MultiMap的示例,而不需要复制其值:

object App extends Application {
  import scala.collection.mutable._
  val seed: Map[String,Set[Int]] = Map("even" -> Set(2,4,6), "odd" -> Set(1,3,5))

  val multiMap = new MapProxy[String,Set[Int]] with MultiMap[String,Int] {
    val self = seed
  }

  multiMap.addBinding("even", 8)
  println(multiMap) // Map(odd -> Set(5, 3, 1), even -> Set(6, 8, 4, 2))
}

请注意,这不能在groupBy(...)的结果上执行,因为需要seed map是可变的,而groupBy(...)返回的是不可变的map。

调用 groupBy() 和可变集合确实会返回一个 mutable.Map。 - Craig P. Motlin
你能演示一下groupBy返回一个可变的Map吗?我得到了一个编译器错误,就像这里所示(http://gist.github.com/245062)。 - Mitch Blevins
我猜它返回的是可变集合的不可变映射?无论如何,我根本不想要一个映射,我想要一个多重映射。 - Craig P. Motlin
groupBy(...)的结果的可变性是相关的,因为将其包装以创建一个“具有MultiMap的MapProxy”而不复制值需要它是可变的。请参见答案中附加的代码。 - Mitch Blevins
谢谢,附录正是我正在寻找的信息。 - Craig P. Motlin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接