Scala有效地将Seq [A]转换为频率映射Map [A​​,Int]

时间:2016-11-29 21:59:58

标签: performance scala

我实现了此功能的四个正确版本。我喜欢半惯用的Scala版本,它运行得更快,更符合类似Java的实现。

groupByAndCount:最干净,最优雅。不幸的是,它很慢。

foldImmutable:完全内部不可变。运行得更慢。

iterateToMutable:简单的可变版本。还是很慢。

iterateToJavaMutable:使用Java(可变)HashMap提供计算功能,因此代码可以避免每个元素迭代的单独get / set函数。

fixedTypeLongCustomMap:这是使用自定义非通用集合it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap并且运行速度最快。

以下是一些jmh基准:

Benchmark                                     Mode  Cnt  Score   Error  Units
FreqMapGenerationJava.fixedTypeLongCustomMap  avgt    5  0.255 ± 0.061   s/op
FreqMapGenerationJava.foldImmutable           avgt    5  3.728 ± 0.318   s/op
FreqMapGenerationJava.groupByAndCount         avgt    5  1.315 ± 0.405   s/op
FreqMapGenerationJava.iterateToJavaMutable    avgt    5  0.654 ± 0.080   s/op
FreqMapGenerationJava.iterateToMutable        avgt    5  1.356 ± 0.240   s/op

这是完整的Scala代码:

  def foldImmutable[A](l: Seq[A]): immutable.Map[A, Int] = {
    def foldF(m: immutable.Map[A, Int], a: A): immutable.Map[A, Int] = {
      m + (a -> (m.getOrElse(a, 0) + 1))
    }

    l.foldLeft(immutable.Map[A, Int]())(foldF)
  }

  def groupByAndCount[A](l: Seq[A]): immutable.Map[A, Int] =
    l.groupBy(x => x).mapValues(l => l.size)

  def iterateToMutable[A](l: Seq[A]): mutable.Map[A, Int] = {
    val m = mutable.Map[A, Int]()
    for (a <- l) {
      m(a) = m.getOrElse(a, 0) + 1
    }
    m
  }

  def iterateToJavaMutable[A](l: Seq[A]): java.util.Map[A, Int] = {
    val m = new java.util.HashMap[A, Int]()
    for (a <- l) {
      m.compute(a, (k, v) => if (v == null) 1 else v + 1)
    }
    m
  }

  def fixedTypeLongCustomMap(l: Seq[Long]): Long2IntOpenHashMap = {
    val m = new Long2IntOpenHashMap
    for (a <- l) {
      m.addTo(a, 1)
    }
    m
  }

2 个答案:

答案 0 :(得分:1)

检查一下:

def foldImmutableAggregate[A](l: Seq[A]) : Map[A, Int] = {
  l.aggregate(Map[A, Int]())({ (sum, ch) => sum ++ Map(ch -> (sum.getOrElse(ch, 0) + 1)) }, (a, b) => a ++ b) 
}

def foldImmutableAggregate[A](l: Seq[A]) : Map[A, Int] = {
  l.par.aggregate(Map[A, Int]())({ (sum, ch) => sum ++ Map(ch -> (sum.getOrElse(ch, 0) + 1)) }, (a, b) => a ++ b) 
}

以块的形式处理Seq

答案 1 :(得分:0)

您是否在可变和不可变集合上尝试过Scala标准库groupBy

on Immuable Seq

scala> Seq(1, 2, 2, 2, 1, 1, 1, 4, 4).groupBy(identity).mapValues(_.size)
res16: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 4 -> 2, 1 -> 4)

on mutable ListBuffer

scala> ListBuffer(1, 2, 1, 2).groupBy(identity).mapValues(_.size)
res17: scala.collection.immutable.Map[Int,Int] = Map(2 -> 2, 1 -> 2)