[Golang] map のイテレートは順序が変わる
知っていたはずなのにハマってしまったので、ちゃんとメモ。
map と順序
map を range
で回したとき、定義した順序で要素を取得できる保証がない (たまたま同じ順序になることはある)。
厳密には、イテレーションの開始位置がランダムに変わる。
たとえば、次のように map を定義する。ふつうは定義した順番に表示されると思うのだけど、
func main() {
mapColor := map[string]string{
"yellow": "黄色",
"blue": "青色",
"red": "赤色",
"black": "黒色",
}
for i, v := range mapColor {
fmt.Println(i, v)
}
}
実は、下のような順序で表示されたりする (毎回違う)。
black 黒色
yellow 黄色
blue 青色
red 赤色
じゃあどうするか
順序を保持するための slice をつくるのが一般的かな。
func main() {
mapColor := map[string]string{
"yellow": "黄色",
"blue": "青色",
"red": "赤色",
"black": "黒色",
}
order = []string{"yellow", "blue", "red", "black"}
for _, o := range order {
fmt.Println(o, mapColor[o])
}
}
なぜ順序が変わるのか
@Linda_pp mapをforで回すと順不同なのに順番に依存するコードを書かないようにあえてランダムにしているそうです。https://t.co/o4F4yhpVHz からの会話と https://t.co/2lMy0LW2dF をご参照ください。
— Hiroaki Nakamura (@hnakamur2) September 29, 2015
ツイートにあるとおり、乱数を使ってあえて順序が変わるようにしているらしい。 map が順序を考慮していないことを明示するためなのだろうか。
でも正直なところ、もし他の言語と同じように Go の map も順序が保証されていたとして、順序が必要な処理を map で書いて困ることってあるのかなと思ってしまう 🤔