Map oluşturmak ve kullanmak
Bir map tipi map[KeyType]ValueType şeklinde yazılır. Bir literal ya da make ile oluşturun, sonra anahtara göre okuyun, yazın ve silin.
Çıktı:
31
3
map[bob:26 cy:40]
2 0
Burada iki kolaylık görünüyor. Eksik bir anahtarı okumak değer tipinin sıfır değerini döndürür (counts['z'] değeri 0'dır); bu da m[k]++ ile saymayı hiçbir hazırlık olmadan mümkün kılar. Ve fmt, map'leri anahtarları sıralı olarak yazdırır; bu hata ayıklamada kullanışlıdır ama dolaşma sırası hakkında hiçbir şey söylemez.
make(map[K]V, n) isteğe bağlı bir boyut ipucu alır. Yaklaşık n girdi için önceden yer ayırır; slice'ların aksine bir map'in geri okuyabileceğiniz bir kapasitesi yoktur.
Bir anahtarın var olup olmadığını kontrol etmek: virgül-ok
Eksik bir anahtar sıfır değer olarak okunduğu için m[k] == 0, "yok" ile "0 olarak saklanmış"ı ayırt edemez. İki değerli biçimi kullanın:
if v, ok := m[k]; ok { ... } biçimi v ve ok'u if'in kapsamında tutar. Go kodundaki en yaygın satırlardan biridir.
Girdileri silmek
delete(m, key) girdiyi kaldırır. Var olmayan bir anahtarı silmek hiçbir şey yapmaz, nil bir map'ten silmek de öyle. Bütün bir map'i boşaltmak için Go 1.21, ayrılmış map'i yeniden kullanılabilsin diye koruyan clear(m)'i ekledi.
Aynı map üzerinde range sırasında girdi silmek serbesttir ve güvenlidir. Döngü ona ulaşmadan silinen bir girdi üretilmez.
Dolaşmak: sıra rastgeledir
for k, v := range m, her girdiyi belirtilmemiş bir sırayla bir kez ziyaret eder. Çalışma zamanı başlangıç noktasını bilerek rastgele yapar, bu yüzden aynı programda aynı map üzerindeki iki döngü çoğu zaman farklı sıra verir. Bunu birkaç kez çalıştırın:
Çıktısı map sırasına bağlı olan her kod, farklı bir çalıştırmayı bekleyen bir hatadır. Yazdırılmış map dolaşmasını karşılaştıran testler bunun klasik örneğidir.
Sıralı anahtarlar
Bir map'i anahtar sırasıyla ziyaret etmek için anahtarları alın, sıralayın ve map'e indeksleyin. Go 1.23, maps ve slices paketlerindeki iterator'larla bunu tek satır yaptı:
Go 1.22 ve öncesinde maps.Keys standart kütüphanede yoktu. Karşılığı bir döngüdür:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
maps içindeki diğer yardımcılar: maps.Values, maps.Clone (sığ bir kopya), maps.Equal, maps.Copy(dst, src) ve maps.DeleteFunc.
Geçerli anahtar tipleri
Anahtarlar == ile karşılaştırılabilir olmalıdır: sayılar, string'ler, boolean'lar, pointer'lar, channel'lar, karşılaştırılabilir tiplerden diziler, tüm alanları karşılaştırılabilir olan struct'lar ve interface değerleri. Slice'lar, map'ler ve fonksiyonlar anahtar olamaz.
map[[]int]bool{} // compile error: invalid map key type []int
map[[2]int]bool{} // fine: arrays are comparable
map[struct{ X, Y int }]string{} // fine: a struct key for a grid position
Birden fazla değere göre anahtarlamanın deyimsel yolu, string'leri birleştirmek yerine bir struct anahtardır.
Interface anahtarları, dinamik tip karşılaştırılabilir olmasa bile derlenir ve sonra çalışma zamanında panic olur: bir map[any]int altına bir []int saklamak runtime error: hash of unhashable type []int ile başarısız olur.
Ondalıklı sayı anahtarları çalışır, ama NaN kendine eşit değildir, bu yüzden bir NaN anahtarı tekrar tekrar eklenebilir ve hiçbir zaman geri okunamaz. Float anahtarlardan kaçının.
Struct map'i
Bir map struct tutabilir, ama map'te saklanan bir struct'ın alanına atama yapamazsınız, çünkü map değerleri adreslenebilir değildir.
Girdiler küçükse ve bir bütün olarak değiştiriliyorsa değerleri seçin. Alanları sık güncelliyorsanız ya da aynı kaydı birkaç yerden paylaşıyorsanız pointer'ları seçin. Pointer'larla eksik bir anahtar nil döndürür, bu yüzden ptrs["nope"].Score panic olur.
Slice map'leri de eleman eklemek için aynı şekilde çalışır: groups[k] = append(groups[k], v) başlatma gerektirmez, çünkü eksik bir anahtar nil bir slice verir ve append nil'i ele alır.
Map'ler referans benzeridir
Bir map değeri paylaşılan veriye işaret eder. Bir map'i atamak ya da bir fonksiyona aktarmak girdileri kopyalamaz: iki değişken de aynı map'i görür.
Bir fonksiyonun, eleman eklediği bir slice'ın aksine, bir map'i döndürmeden doldurabilmesinin nedeni budur.
Nil map panic'i
Bir map'in sıfır değeri nil'dir. Nil bir map boş bir map gibi okunur, ama ona yazmak panic'e yol açar.
Çıktı:
0 0
recovered: assignment to entry in nil map
Pratikte canınızı yakan struct durumudur. Map alanlarını bir kurucuda (func NewCache() *Cache { return &Cache{data: map[string]string{}} }) ya da ilk yazmadan önce tembel olarak başlatın.
Eşzamanlı erişim
Map'ler eşzamanlı kullanım için güvenli değildir. Bir goroutine yazarken bir başkası okur ya da yazarsa, çalışma zamanı programı fatal error: concurrent map writes (ya da concurrent map read and map write) ile durdurabilir. Bu bir panic değil ölümcül bir hatadır, bu yüzden recover onu yakalayamaz.
Map'i bir mutex ile koruyun:
Bu her zaman 50 50 yazdırır. Okumalar yazmalardan çok daha fazlaysa sync.RWMutex kullanın. sync.Map iki dar durum için vardır (bir kez yazılıp çok kez okunan anahtarlar ya da ayrık anahtarlar üzerinde çalışan goroutine'ler); geri kalan her şey için bir mutex ve düz bir map daha basittir ve genellikle daha hızlıdır. Daha fazlası mutex sayfasında.
Hızlı başvuru
| İşlem | Kod |
|---|---|
| Oluşturma | m := map[string]int{} ya da make(map[string]int) |
| Ekleme ya da güncelleme | m[k] = v |
| Okuma (yoksa sıfır) | v := m[k] |
| Varlık kontrolü | v, ok := m[k] |
| Silme | delete(m, k) |
| Hepsini kaldırma | clear(m) (Go 1.21) |
| Boyut | len(m) |
| Sıralı anahtarlar | slices.Sorted(maps.Keys(m)) (Go 1.23) |
| Kopyalama | maps.Clone(m) |
| Karşılaştırma | maps.Equal(a, b) |
Değerleri struct{} olan bir map, Go'nun set tipidir da; bkz. set'ler.
Sık yapılan hatalar
- Nil bir map'e yazmak. Struct'lardaki map alanları dahil her zaman
makeile oluşturun. - Dolaşma sırasına güvenmek. Anahtarları sıralayın.
- Varlık testi olarak
m[k] != 0kullanmak. Virgül-ok kullanın. - Bir struct alanını
m[k].Fieldüzerinden değiştirmek. Dışarı kopyalayıp geri yazın ya da pointer saklayın. - Bir map'i kilit olmadan goroutine'ler arasında paylaşmak. Çökme recover edilemez.
Sıkça Sorulan Sorular
Bir Go map'inde bir anahtarın var olup olmadığı nasıl kontrol edilir?
Aramanın iki değerli biçimini kullanın: v, ok := m[key]. Anahtar varsa ok true, yoksa false olur; bu durumda v sıfır değerdir. Yalnızca m[key]'i okumak, eksik bir anahtarı sıfır değerle saklanmış bir anahtardan ayırt edemez.
Go map dolaşma sırası neden rastgele?
Dil bir sıra tanımlamaz ve çalışma zamanı, programlar tek bir sıraya bağımlı hale gelmesin diye her range'i bilerek rastgele bir konumdan başlatır. Anahtar sırasıyla dolaşmak için anahtarları toplayıp sıralayın: for _, k := range slices.Sorted(maps.Keys(m)) (Go 1.23).
Go'da bir map'in tüm anahtarları nasıl alınır?
Go 1.23'ten beri maps.Keys(m) bir iterator döndürür; onu slices.Collect(maps.Keys(m)) ile bir slice'a, slices.Sorted(maps.Keys(m)) ile sıralı bir slice'a çevirin. 1.23'ten önce for k := range m ile dönüp her anahtarı bir slice'a ekleyin.
Bir map'e yazmak neden "assignment to entry in nil map" ile panic oluyor?
Map değişkeni tanımlanmış ama hiç oluşturulmamış: var m map[string]int nil'dir. Nil bir map'ten okumak sıfır değerler döndürür, ama yazmak panic'e yol açar. Önce m = make(map[string]int) ya da bir literal m := map[string]int{} ile oluşturun. Bir struct içindeki map alanı da aynı başlatmayı ister.
Go map'leri eşzamanlı kullanım için güvenli mi?
Hayır. Eşzamanlı yazmalar ya da okumalarla eşzamanlı bir yazma, programı recover'ın yakalayamadığı fatal error: concurrent map writes ile çökertebilir. Map'i bir sync.Mutex ya da sync.RWMutex ile koruyun veya tasarlandığı belirli durumlar için sync.Map kullanın.