Ответ
В Go для реализации счётчика очень удобно использовать map[T]int
. Самый идиоматичный способ инкрементировать значение — это прямой инкремент. Это работает, потому что нулевое значение для типа int
— это 0
, и при первом обращении к несуществующему ключу он будет неявно создан с этим значением.
1. Простой способ (не потокобезопасный)
counts := make(map[string]int)
// Инкремент счётчика для ключа "apple"
// Если ключ не существует, Go вернёт нулевое значение (0), и операция станет 0++
counts["apple"]++
fmt.Println(counts["apple"]) // Выведет: 1
2. Конкурентный доступ
Стандартный map
не является потокобезопасным. При одновременном доступе из нескольких горутин возникнет состояние гонки. Для решения этой проблемы есть два основных подхода:
sync.Mutex
: Классический способ защиты разделяемого ресурса с помощью мьютекса. Подходит для большинства сценариев.
import "sync"
var mu sync.Mutex
counts := make(map[string]int)
// Внутри горутины
mu.Lock() // Блокируем доступ
counts["key"]++
mu.Unlock() // Освобождаем
sync.Map
: Специализированный тип, оптимизированный для сценариев, где количество чтений значительно превышает количество записей. Он предоставляет атомарные операции и не требует явных блокировок.
import "sync"
var counts sync.Map
// Инкремент
actual, _ := counts.LoadOrStore("key", 1)
if v, ok := actual.(int); ok && v > 1 {
counts.Store("key", v+1)
}
// Обратите внимание: работа с sync.Map для счётчика менее тривиальна, чем с мьютексом.