加速迭代文本并创建 Map[Tuple2[String, String], Int] Scala 的方法

Speeding up a method that iterates through text and creates a Map[Tuple2[String, String], Int] Scala

我在 scala 程序中有一个创建 Map[Tuple2[String, String], Int] 的方法,但它 运行 非常慢并且无法处理很多文本。我似乎无法弄清楚如何加快速度并提高效率。任何建议将不胜感激。

def createTuple(words: List[String]): Map[Tuple2[String, String], Int] = {
    var pairCountsImmutable = Map[Tuple2[String, String], Int]()
    val pairCounts = collection.mutable.Map(pairCountsImmutable.toSeq: _*)
    var i = 0
    for (i <- 0 to words.length - 2) {
        val currentCount: Int = pairCounts.getOrElse((words(i), words(i + 1)), 0)
        if (pairCounts.exists(_ == (words(i), words(i + 1)) -> currentCount)) {
            var key = pairCounts(words(i), words(i + 1))
            key = key + 1
            pairCounts((words(i), words(i + 1))) = key
        } else {
            pairCounts += (words(i), words(i + 1)) -> 1
        }
    }
    var pairCountsImmutable2 = collection.immutable.Map(pairCounts.toList: _*)
    return pairCountsImmutable2
}

你的大问题是 words 是一个 List,而你却用 words(i) 索引它。那很慢。将其更改为 Vector 或修改算法以不使用索引。

此外,pairCounts.exists 很慢,您应该尽可能使用 contains,因为它在地图上是常数时间。

更新

我无耻地借鉴了 TRuhland 的答案,给出了我的答案的改进版本,它不会因空列表或单元素列表而失败:

def createTuple(words: List[String]): Map[Tuple2[String, String], Int] =
  words
    .zip(words.drop(1))
    .groupBy(identity)
    .mapValues(_.length)

原创

您似乎在计算相邻的单词对是一个单词列表。如果是这样,这样的事情应该有效:

def createTuple(words: List[String]): Map[Tuple2[String, String], Int] =
  words
    .sliding(2)
    .map(l => (l(0), l(1)))
    .toList
    .groupBy(identity)
    .mapValues(_.length)

工作原理如下

  1. sliding(2) 创建相邻词对列表
  2. map 将每对从 List 变成元组
  3. groupBy 将具有相同值的元组分组
  4. mapValues统计每对有相同值的对数

这可能不是您想要的,但希望它能让您了解如何完成。

作为一般规则,不要使用索引遍历列表,而是尝试将列表转换为可以遍历值的内容。

尽量不要逐个创建 Maps。使用 groupBytoMap.

如果我们首先将您的代码简化为本质:

def createTuple(words: List[String]): Map[(String, String), Int] = {
    val pairCounts = collection.mutable.Map[(String, String), Int]()
    for (i <- 0 until words.length - 1) {
      val pair = (words(i), words(i + 1))
      pairCounts += (pair  -> (pairCounts.getOrElse(pair, 0) + 1))
    }
    pairCounts.toMap
  }

为了提高速度,不要在列表上使用索引(如其他地方所述):

def createTuple(words: List[String]): Map[(String, String), Int] = {
  val map = collection.mutable.Map[(String, String), Int]()
  words
    .zip(words.tail)
    .foreach{ pair => 
       map += (pair -> (map.getOrElse((pair, 0) + 1)) }
  map.toMap
}