Как увеличить значение данного ключа с помощью только одного поиска по карте?

Скажем, у меня есть карта:

 var inventory = mutableMapOf("apples" to 1, "oranges" to 2)

и я хочу увеличить количество яблок на единицу.

Однако это не работает:

inventory["apples"]!!++ // Error:(9, 4) Variable expected

Это тоже:

var apples = inventory["apples"]!!
++apples
println(inventory["apples"]) // prints "1" => the value was not incremented.

Что удивительно, поскольку в Kotlin числа помещаются в рамки, когда они хранятся в экземплярах общих объектов.. Не ожидал, что копия делается.

Кажется, единственный способ сделать что-то вроде:

var apples = inventory["apples"]!!
++apples
inventory["apples"] = apples
println(inventory["apples"]) 

В обоих случаях используются два поиска на карте, и они чрезвычайно уродливы и длинны.

Есть ли способ увеличить значение данного ключа, используя только один поиск?

Кроме того, может кто-нибудь объяснить, почему первые два метода не работают?


person Martin Drozdik    schedule 14.12.2018    source источник
comment
Никогда не использовал Kotlin, но, посмотрев ваш код, я бы попробовал что-то вроде inventory["apples"] = ++inventory["apples"]!!   -  person Sedrick    schedule 14.12.2018


Ответы (5)


Невозможно увеличить значение только с одним get и без put. После этого всегда нужно делать put, потому что Int неизменяем. Метод inc() (++ - это сокращение) возвращает новое значение, которое необходимо сохранить на карте. Это не меняет сохраненное значение.

Ты можешь использовать

inventory.computeIfPresent("apples") { _, v -> v + 1 }

Чтобы записать его в одну строку, но если вы посмотрите на реализацию, он также сделает get(), затем вычислит новое значение и затем сделает put(). Таким образом, вы можете сохранить строку кода, которую нужно написать, но вы не сэкономите циклы процессора на времени выполнения.

person Simulant    schedule 14.12.2018

Попробуй это:

inventory.computeIfPresent("apples") { _, v -> v + 1 }

Первые два метода не работают, потому что, когда вы извлекаете значение из карты, вы получаете примитивный тип, в вашем случае Integer, который является неизменным. Когда вы меняете значение Integer, вы просто указываете свою переменную на новый экземпляр Integer, вы не обновляете тот экземпляр Integer, на который вы ранее указывали.

person Yoni Gibbs    schedule 14.12.2018
comment
Спасибо! Это работает, но реализация выполняет два поиска под капотом. Как вы думаете, этого можно добиться с помощью только одного поиска? - person Martin Drozdik; 14.12.2018
comment
@MartinDrozdik метод computeIfPresent выполняет два поиска, но если карта создается hashMapOf, он выполняет только один поиск. Смотрите мой ответ. - person DodgyCodeException; 14.12.2018

Поскольку целые числа заключены в рамки на картах, Integer - это примитивный класс-оболочка, а примитивные классы-оболочки неизменяемы в Java, кажется, что вы хотите изменить неизменяемый объект, что, к сожалению, невозможно, по крайней мере, никакими обычными или рекомендуемыми способами.

Взгляните на Самый эффективный способ увеличения значения карты в Java, где есть несколько предложений о том, как улучшить производительность приращения целочисленных значений в картах, а также одно явно реализует желаемое поведение с помощью класса-оболочки MutableInt, который также оказывается самым быстрым (~ На 20% быстрее, чем базовый уровень).

person perovic    schedule 14.12.2018

Метод computeIfPresent в java.util.HashMap выполняет только один поиск. Итак, вы можете использовать:

var inventory = hashMapOf("apples" to 1, "oranges" to 2)
inventory.computeIfPresent("apples") { _, v -> v + 1 }  // Only 1 lookup!

Примечание: это деталь реализации. Он выполняет один поиск в текущей версии Kotlin (1.3.11) с использованием конкретной реализации Java (Oracle HotSpot 11.0.1). Другие конфигурации могут использовать или не использовать другие способы.

person DodgyCodeException    schedule 14.12.2018

Другая идея

// Declaration
val map = mutableMapOf<String, Int>()
// Increment
map.put("x", map.getOrPut("x"){ 1 } + 1)
person Lucas    schedule 11.04.2020