Могу ли я одновременно обновлять и извлекать элемент на карте без двух индексных вызовов?

#go #hashmap

#Вперед #hashmap

Вопрос:

У меня есть карта с целочисленными значениями. Я хочу обновить значение, а затем проверить, превышает ли обновленное значение пороговое значение.

Если бы мне не нужно было проверять новое значение, я бы просто сделал

 map[key]  = 1
  

Очевидным изменением, если я захочу проверить новое значение, будет:

 old_val := map[key]
new_val := old_val   1
map[key] = new_val
if (new_val > threshold) {
    return
}
  

Однако при этом выполняется два индексных вызова карты, что не обязательно является операцией с постоянным временем.

Что я хотел бы сделать, это что-то вроде:

 val_p := amp;(map[key])
*(val_p)  = 1
if (*(val_p) > threshold) {
    return
}
  

Однако карты GoLang не являются адресуемыми по дизайну, потому что адреса, очевидно, могут меняться (хотя в данном случае это не изменилось бы, потому что я не изменяю карту).

Есть ли какая-нибудь функция, которую я мог бы использовать для обновления элемента на карте, которая возвращает новое значение? Специально для целых чисел?

Комментарии:

1. Нет, не существует. Вы измерили проблему с производительностью и составили профиль, чтобы сузить ее до стоимости второго поиска по карте, или это чисто умозрительная оптимизация?

2. Нет, это просто предположение, и я сомневаюсь, что это существенно повлияет на производительность. Немного раздражает, что это то, чему препятствует дизайн, который оказывает ощутимое (хотя, вероятно, небольшое) влияние на производительность.

3. Хорошо, что такие волшебные / перегруженные вещи предотвращены дизайном, IMO. Это делает код намного понятнее для чтения.

4. Если вы не измерили и не профилировали проблему производительности, вы не можете утверждать, что это оказывает ощутимое влияние. Вы предполагаете, что это возможно. Попытки исправить проблему, о существовании которой вы не знаете, обычно контрпродуктивны.

5. Было бы неплохо, если бы существовал способ передать функцию на карту вместе с ключом, а затем обновить ее и вернуть связанное значение. Это определенно то, что должно быть возможно.

Ответ №1:

Вы не можете сделать это с «простыми» значениями по причинам, которые вы упомянули в вопросе.

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

Например:

 const threshold = 3

m := map[string]*int{
    "one": new(int),
}

for {
    p := m["one"]
    fmt.Println("data=", *p)
    *p  
    if *p > threshold {
        fmt.Println("threshold reached", *p)
        break
    }
}
  

Это выводит (попробуйте это на игровой площадке Go):

 data= 0
data= 1
data= 2
data= 3
threshold reached 4
  

Хотя обратите внимание, что это может быть не быстрее из-за косвенных указателей. Измерьте, является ли это важной частью вашего приложения.

Комментарии:

1. И сборщику мусора (GC) придется следовать всем этим указателям.