Я хотел сравнить характеристики производительности immutable.Map и mutable.Map в Scala для аналогичной операции (а именно, слияние многих карт в один. См. этот вопрос). У меня есть то, что похоже на аналогичные реализации для изменяемых и неизменяемых карт (см. Ниже).
В качестве теста я сгенерировал список, содержащий 1,000,000 отдельных элементов Map [Int, Int] и передал этот список в те функции, которые я тестировал. С достаточной памятью результаты были неудивительными: ~ 1200 мс для mutable.Map, ~ 1800 мс для неизменяемых .Map и ~ 750 мс для императивной реализации с использованием mutable.Map - не уверен, что объясняет огромную разницу там, но не стесняйтесь прокомментируйте это тоже.
Что меня немного удивило, возможно, потому, что я немного толще, это то, что с конфигурацией запуска по умолчанию в IntelliJ 8.1 обе изменчивые реализации попали в OutOfMemoryError, но неизменная коллекция этого не сделала. Неизменяемый тест действительно закончился, но он сделал это очень медленно - это занимает около 28 секунд. Когда я увеличил максимальную память JVM (примерно до 200 МБ, не уверен, где находится порог), я получил результаты выше.
В любом случае, вот что я действительно хочу знать:
Почему переменные реализации заканчиваются из памяти, но неизменная реализация не работает? Я подозреваю, что неизменяемая версия позволяет сборщику мусора запускать и освобождать память до того, как выполняются изменяемые реализации - и все эти коллекции мусора объясняют медленность неизменного прогона с низкой памятью, но я хотел бы получить более подробное объяснение, чем это.
Реализации ниже. (Примечание: я не утверждаю, что это наилучшие варианты реализации. Не стесняйтесь предлагать улучшения.)
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
}