Рассмотрим простую проблему использования изменяемой карты для отслеживания вхождений/подсчетов, то есть с помощью:
val counts = collection.mutable.Map[SomeKeyType, Int]()
Мой текущий подход к увеличению количества:
counts(key) = counts.getOrElse(key, 0) + 1
// or equivalently
counts.update(key, counts.getOrElse(key, 0) + 1)
Это как-то немного неуклюже, потому что я должен указать ключ дважды. Что касается производительности, я бы также ожидал, что key
должен быть расположен дважды на карте, чего я бы хотел избежать. Интересно, что эта проблема доступа и обновления не возникла бы, если бы Int
предоставил некоторый механизм для изменения самого себя. Переход от класса Int
к Counter
который обеспечивает функцию increment
, например, разрешит:
// not possible with Int
counts.getOrElseUpdate(key, 0) += 1
// but with a modifiable counter
counts.getOrElseUpdate(key, new Counter).increment
Как-то я всегда ожидаю, что буду иметь следующую функциональность с изменяемой картой (несколько похожей на transform
но без возврата новой коллекции и на конкретный ключ со значением по умолчанию):
// fictitious use
counts.updateOrElse(key, 0, _ + 1)
// or alternatively
counts.getOrElseUpdate(key, 0).modify(_ + 1)
Однако, насколько я вижу, такой функции не существует. Разве это не имеет смысла вообще (производительность и синтаксис мудрый) иметь такую возможность f: A => A
на месте? Наверное, я просто что-то пропустил здесь... Наверное, должно быть какое-то лучшее решение этой проблемы, чтобы сделать такую функциональность ненужной?
Обновить:
Я должен был уточнить, что мне известно о withDefaultValue
но проблема остается прежней: выполнение двух запросов еще в два раза медленнее одного, независимо от того, является ли это операцией O (1) или нет. Честно говоря, во многих ситуациях я был бы более чем счастлив добиться ускорения фактора 2. И, очевидно, конструкция закрытия модификации часто может перемещаться за пределы цикла, поэтому imho это не большая проблема по сравнению с запуском без необходимости дважды.