Go のマップのイテレーション(for range)
マップの全要素を処理するには for range を使う。スライスのイテレーションと似ているが、いくつか重要な違いがある。
基本的なイテレーション
for range でキーと値を同時に取得できる。
ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Charlie": 35,
}
for name, age := range ages {
fmt.Printf("%s is %d years old\n", name, age)
}出力例:
Bob is 25 years old
Alice is 30 years old
Charlie is 35 years old順序は保証されない
マップのイテレーション順序は不定だ。同じマップでも実行するたびに順序が変わる可能性がある。
for i := 0; i < 3; i++ {
fmt.Println("--- Run", i+1, "---")
for k, v := range ages {
fmt.Println(k, v)
}
}Go は意図的にマップの順序をランダム化している。順序に依存したコードを書かないようにするためだ。
キーだけを取得
値が不要な場合は、キーだけを受け取れる。
for name := range ages {
fmt.Println(name)
}値だけを取得
キーが不要な場合は、ブランク識別子を使う。
for _, age := range ages {
fmt.Println(age)
}ソートしてイテレーション
順序が必要な場合は、キーをスライスに取り出してソートする。
import "sort"
ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Charlie": 35,
}
// キーをスライスに取り出す
names := make([]string, 0, len(ages))
for name := range ages {
names = append(names, name)
}
// ソート
sort.Strings(names)
// ソート順でイテレーション
for _, name := range names {
fmt.Printf("%s: %d\n", name, ages[name])
}出力:
Alice: 30
Bob: 25
Charlie: 35イテレーション中の変更
イテレーション中に要素を追加・削除することは可能だが、挙動が複雑になる。
m := map[int]bool{1: true, 2: true, 3: true}
for k := range m {
if k == 2 {
delete(m, k) // 削除はOK
}
}
fmt.Println(m) // map[1:true 3:true]削除は安全に行えるが、追加した要素がそのイテレーションで処理されるかどうかは不定だ。
m := map[int]bool{1: true}
for k := range m {
m[k+10] = true // 追加
if k > 100 {
break // 無限ループ防止
}
}
// 結果は不定イテレーション中の追加は避けるか、新しいマップに書き込む方が安全だ。
空のマップのイテレーション
空のマップや nil マップをイテレーションしても panic は起きない。単にループが実行されないだけだ。
var m map[string]int // nil マップ
for k, v := range m {
fmt.Println(k, v) // 実行されない
}
fmt.Println("Done") // 正常に出力されるこれにより、マップが空かどうかを事前にチェックする必要がない。