Ответ 1
Короткий ответ
Это определенно не ошибка.
Длинный ответ
Поведение withDefaultValue
заключается в том, чтобы сохранить значение по умолчанию (в вашем случае, измененную карту) внутри Map, которое будет возвращено в случае, если ключ не существует. Это не то же самое, что значение, которое вставляется в карту, когда ключ не найден.
Посмотрите внимательно на то, что происходит. Это будет легче понять, если мы потянем карту по умолчанию как отдельную переменную, чтобы мы могли проверить ее по своему усмотрению; назовем его default
import collection.mutable.HashMap
val default = HashMap.empty[Int,Int].withDefaultValue(3)
Итак, default
- изменяемое отображение (которое имеет собственное значение по умолчанию). Теперь мы можем создать m
и дать default
как значение по умолчанию.
import collection.mutable.{Map => MMap}
val m = HashMap.empty[Int, MMap[Int,Int]].withDefaultValue(default)
Теперь, когда m
получает доступ с отсутствующим ключом, он возвращает default
. Обратите внимание, что это то же самое поведение, что и у вас, потому что withDefaultValue
определяется как:
def withDefaultValue (d: B): Map[A, B]
Обратите внимание, что это d: B
, а не d: => B
, поэтому он не будет создавать новую карту каждый раз, когда будет доступен доступ по умолчанию; он вернет тот же самый точный объект, который мы назвали default
.
Итак, посмотрим, что получится:
m(1) // Map()
Поскольку ключ 1 не находится в m
, возвращается значение по умолчанию, default
. default
в это время представляет собой пустую карту.
m(1)(2) = 5
Так как m(1)
возвращает default
, эта операция сохраняет 5 как значение для ключа 2 в default
. На карту m
ничего не записывается, потому что m(1)
разрешает default
, которая является отдельной Картой полностью. Мы можем проверить это, просмотрев default
:
default // Map(2 -> 5)
Но, как мы сказали, m
остается неизменным
m // Map()
Теперь, как добиться того, чего вы действительно хотели? Вместо использования withDefaultValue
вы хотите использовать getOrElseUpdate
:
def getOrElseUpdate (key: A, op: ⇒ B): B
Обратите внимание, как мы видим op: => B
? Это означает, что аргумент op
будет переучитываться каждый раз, когда это необходимо. Это позволяет нам помещать новую карту в нее и иметь отдельную новую карту для каждого недействительного ключа. Давайте посмотрим:
val m2 = HashMap.empty[Int, MMap[Int,Int]]
Здесь не требуются значения по умолчанию.
m2.getOrElseUpdate(1, HashMap.empty[Int,Int].withDefaultValue(3)) // Map()
Ключ 1 не существует, поэтому мы вставляем новый HashMap и возвращаем это новое значение. Мы можем проверить, что он был вставлен так, как мы ожидали. Обратите внимание: 1 сопоставляется с недавно добавленной пустой картой и что они не добавлены нигде из-за описанного выше поведения.
m2 // Map(1 -> Map())
Аналогично, мы можем обновить карту, как ожидалось:
m2.getOrElseUpdate(1, HashMap.empty[Int,Int].withDefaultValue(1))(2) = 6
и убедитесь, что он был добавлен:
m2 // Map(1 -> Map(2 -> 6))