Goでは、mapはハッシュテーブルを表す組み込みのデータ型です。
他のプログラミング言語では、mapはdictや連想配列、キーと値のストアとして知られているかもしれません。
こうした概念に馴染みがなければ、mapは辞書のようなものだと考えてみてください。辞書では、各単語がkeyで、その定義がmapのelementにあたります。
構文的には、mapは次のようになります。
map[KeyType]ElementType
KeyTypeは比較可能な型である必要がありますが、ElementTypeはGoで有効な任意の型にできます。つまり、プリミティブな変数からスライスまで、どんなものでも格納できます。
Goのmapは順序を持たないことを覚えておくことが大切です。mapをループして要素を出力しようとすると、要素がランダムな順序で出力されるのを見て驚くかもしれません(よければ試してみてください)。
また、各キーは一意であることも重要です。同じキーを2回代入すると、対応するキーの値が上書きされます。
mapは参照型です。つまり、mapを渡してもGoはmap全体をコピーしません。
代わりにGoが行うのは、mapのポインターのコピーです。これにより、mapを関数や変数に渡すのが低コストになります。
初期化されていないmapの値はnilです。
mapは次のように定義できます(これはnil mapとも呼ばれます)。
var foo map[string]int
mapを初期化するには、次のようにします。
// With map literal
foo := map[string]int{}
または
// or with make function
foo := make(map[string]int)
nil mapは初期化されたmapとは異なります。nil mapに書き込むと、ランタイムエラーが発生します。
panic: assignment to entry in nil mapそのため、使う前にmapを初期化することが大切です
mapでできる操作をいくつか紹介します。
// Add a value in a map with the `=` operator:
foo["bar"] = 42
// Here we update the element of `bar`
foo["bar"] = 73
// To retrieve a map value, you can use
baz := foo["bar"]
// To delete an item from a map, you can use
delete(foo, "bar")
mapに存在しないキーの値を取得しようとすると、値の型のゼロ値が返されます。
これは、ElementTypeのデフォルト値(たとえばintなら0)が有効な値である場合に、特に混乱を招くかもしれません。
mapにキーが存在するかどうかを確認するには、次のようにします。
value, exists := foo["baz"]
// If the key "baz" does not exist,
// value: 0; exists: false
これまで見てきたように、mapが初期化されているかどうかを判別するのは簡単で、単純にnilと比較すればよいのですが、他のmap同士ではどうでしょうか?
mapは比較可能ではないので==演算子は使えませんが、reflectパッケージに助けがあります。それはDeepEqualと呼ばれるもので、次のように使えます。
import "reflect"
// ....
equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true
でも待ってください。mapが比較可能でないのなら、なぜnilとは比較できるのでしょうか? 仕様がこれについて例外を設けています。比較可能の仕様をご覧ください。
最後に、複数のゴルーチンからmapに書き込もうとすると、レース検出器が作動します。GoのドキュメントとGoブログをご覧ください。
別の方法として、この問題を回避するためにsync.Mapやatomic、mutexを使うこともできます。