2012-03-13 127 views
1

我需要处理大量表示人的记录(数百万)。我想创建一个基于出生年份的分区,然后分别处理每个组。我正在尝试创建一个功能性解决方案(无/最小可变数据),以便它可以线程安全并且可以并行化。在Scala中将Seq的可变Map转换为IndexedSeq的不可变映射

对于我的第一次尝试,我创建了一个尾递归函数,它建立了一个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))) 
    } 
} 

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

我想通过内部构建一个可变的Map[Int, List[Person]]然后高效地将其转换为不可变的Map[Int, IndexedSeq[Person]]作为返回值来实现此目的。

如何以可能的最有效方式将List项目的可变Map转换为不可变的Map[Int, IndexedSeq[Person]]?请注意,每个出生年龄组的人群没有特定的顺序。

+0

一个小问题,可能是不相关的:因为你在出生年份进行分区,所以AFAICT你永远不会在这个线程之外传递地图本身。 – 2012-03-13 12:49:57

回答

6

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

def groupByYearOfBirth(people: Seq[Person]) = people.groupBy(_.yearofBirth) 

编辑:有违我最初的命题,不使用.mapValues(_.toIndexedSeq)to provide an IndexedSeq`。丹尼尔解释了为什么在下面的评论。

+0

我没有使用你的技术,因为我不知道它。 :-)看起来它会**完全**我需要的。斯卡拉不是很棒?!谢谢。 – Ralph 2012-03-13 12:13:03

+0

是的,它在scala中总是一样的:首先仔细查看API,大多数情况下,它都包含你所需要的。 ;) – Nicolas 2012-03-13 12:16:38

+2

不要*使用'mapValues'这种方式! 'mapValues'的实现就像一个视图,这意味着每次访问该值时都会应用该转换。鉴于'Seq'和'IndexedSeq'之间的区别,很可能这样做并不会带来任何好处。使用普通的'map'来创建一个新的'Map'。 – 2012-03-13 16:18:53

相关问题