🚧

[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])
	}
}

なぜ順序が変わるのか


ツイートにあるとおり、乱数を使ってあえて順序が変わるようにしているらしい。 map が順序を考慮していないことを明示するためなのだろうか。

でも正直なところ、もし他の言語と同じように Go の map も順序が保証されていたとして、順序が必要な処理を map で書いて困ることってあるのかなと思ってしまう 🤔