将可变的Seq Map转换为Scala中的IndexedSeq的不可变Map

Ral*_*lph 1 scala map immutability

我需要处理代表人的大量记录(数百万).我想根据出生年份创建一个分区,然后分别处理每个组.我正在尝试创建一个功能性解决方案(没有/最小可变数据),因此它将是线程安全的并且可以并行化.

在我的第一次尝试中,我创建了一个尾递归函数,该函数构建了一个Map[Int, IndexedSeq]将每个出生年份映射到一系列人员记录的函数.我需要一个索引序列,因为我将对每个组中的人进行随机访问.这是我的代码:

@tailrec
def loop(people: Seq[Person],
         map: Map[Int, IndexedSeq[Person]] = Map()): Map[Int, IndexedSeq[Person]] = {
  if (people.isEmpty) map
  else {
    val person = people.head
    val yearOfBirth = person.yearOfBirth
    val seq = map.getOrElse(yearOfBirth, IndexedSeq())
    loop(people.tail, map + (yearOfBirth -> (seq :+ person)))
  }
}
Run Code Online (Sandbox Code Playgroud)

这有效,但效率不高.通过允许少量非常局部化的可变性,我可以做得更好.如果所有可变变量都在堆栈上,只要输出Map是不可变的,代码仍然是线程安全的.

我想通过内部构建一个mutable来实现它Map[Int, List[Person]],然后有效地将它转换为一个不可变Map[Int, IndexedSeq[Person]]的返回值.

我怎么能转换可变MapList项目不可变Map[Int, IndexedSeq[Person]]的最有效的方式可能吗?请注意,每个出生年份组的人员没有特别的顺序.

Nic*_*las 6

你为什么不使用特质的groupBy功能Seq?(文档在这里:http://www.scala-lang.org/api/current/index.html#scala.collection.Seq)

def groupByYearOfBirth(people: Seq[Person]) = people.groupBy(_.yearofBirth)
Run Code Online (Sandbox Code Playgroud)

编辑:与我的初始命题相反,不要使用.mapValues(_.toIndexedSeq) to provide anIndexedSeq`.Daniel在下面的评论中解释了为什么.

  • 做*不*那样使用`mapValues`!`mapValues`的实现就像一个视图,这意味着每次访问该值时它都会应用该转换.鉴于`Seq`和`IndexedSeq`之间的区别,很可能这样做不会获得任何东西.使用普通的`map`来创建一个新的`Map`. (2认同)