我想比较Scala中的immutable.Map和mutable.Map的性能特征,以进行类似的操作(即将许多地图合并为一个地图。请参阅this question)。我有可变和不可变映射的类似实现(见下文)。
作为测试,我生成了一个包含1,000,000单项Map [Int,Int]的List,并将此列表传递给我正在测试的函数。有了足够的内存,结果就不足为奇了:对于mutable.Map来说是~1200ms,对于immutable.Map来说是~1800ms,对于使用mutable.Map的命令式实现来说是~750ms - 不知道是什么导致了那里的巨大差异,但是随意对此也有评论。
让我感到惊讶的是,也许是因为我有点厚,是因为IntelliJ 8.1中的默认运行配置,两个可变实现都遇到了OutOfMemoryError,但是不可变集合没有。不可变测试确实完成了,但它的速度非常慢 - 大约需要28秒。当我增加最大JVM内存(大约200MB,不确定阈值在哪里)时,我得到了上面的结果。
无论如何,这是我真正想知道的:
为什么可变实现耗尽了内存,但不可变实现却没有?我怀疑不可变版本允许垃圾收集器运行并在可变实现之前释放内存 - 并且所有这些垃圾收集都解释了不可变的低内存运行的缓慢 - 但我想要更详细的解释。
以下实施。 (注意:我并不认为这些是可能的最佳实现。请随意提出改进建议。)
def mergeMaps[A,B](func: (B,B) => B)(listOfMaps: List[Map[A,B]]): Map[A,B] =
(Map[A,B]() /: (for (m <- listOfMaps; kv <-m) yield kv)) { (acc, kv) =>
acc + (if (acc.contains(kv._1)) kv._1 -> func(acc(kv._1), kv._2) else kv)
}
def mergeMutableMaps[A,B](func: (B,B) => B)(listOfMaps: List[mutable.Map[A,B]]): mutable.Map[A,B] =
(mutable.Map[A,B]() /: (for (m <- listOfMaps; kv <- m) yield kv)) { (acc, kv) =>
acc + (if (acc.contains(kv._1)) kv._1 -> func(acc(kv._1), kv._2) else kv)
}
def mergeMutableImperative[A,B](func: (B,B) => B)(listOfMaps: List[mutable.Map[A,B]]): mutable.Map[A,B] = {
val toReturn = mutable.Map[A,B]()
for (m <- listOfMaps; kv <- m) {
if (toReturn contains kv._1) {
toReturn(kv._1) = func(toReturn(kv._1), kv._2)
} else {
toReturn(kv._1) = kv._2
}
}
toReturn
}
答案 0 :(得分:24)