Go言語の強力なデータ構造であるマップと、その反復処理に便利なrangeの使い方を学びます。
1. マップ(map)とは?
マップは、Go言語に組み込まれている便利なデータ構造の一つで、キー(key)と値(value)のペアを格納します。他の言語では連想配列、ハッシュテーブル、ディクショナリなどと呼ばれるものに似ています。
キーを使って対応する値を高速に検索、追加、削除できるのが特徴です。配列やスライスがインデックス(整数)で要素にアクセスするのに対し、マップは様々な型のデータをキーとして使用できます(ただし、キーは比較可能な型である必要があります)。
==
や !=
演算子で比較可能な型(数値型、文字列型、ポインタ型、配列型、比較可能なフィールドのみを持つ構造体など)を使用できます。スライス、マップ、関数などはキーとして使用できません。 2. マップの基本的な使い方
2.1. マップの宣言と初期化
マップを使うには、まず宣言と初期化を行う必要があります。主な方法は2つあります。
a) `make`関数を使う方法:
make
関数を使って空のマップを作成します。キーの型と値の型を指定します。
package main
import "fmt"
func main() { // string型のキーとint型の値を持つマップを宣言・初期化 scores := make(map[string]int) fmt.Println(scores) // 出力: map[] (空のマップ) fmt.Println(scores == nil) // 出力: false (makeで初期化されたマップはnilではない) // var で宣言しただけだと nil マップになる var nilMap map[string]int fmt.Println(nilMap == nil) // 出力: true // nilMap["key"] = 1 // これはランタイムパニックを引き起こす!
}
b) マップリテラルを使う方法:
マップの宣言と同時に初期値を設定できます。
package main
import "fmt"
func main() { // 宣言と同時に初期化 colors := map[string]string{ "red": "#FF0000", "green": "#00FF00", "blue": "#0000FF", // 末尾のカンマはあってもなくてもOK (推奨) } fmt.Println(colors) // 出力: map[blue:#0000FF green:#00FF00 red:#FF0000] (順序は保証されない)
}
2.2. 要素の追加と更新
マップに要素を追加したり、既存の要素の値を更新するには、マップ変数[キー] = 値
という構文を使います。
package main
import "fmt"
func main() { scores := make(map[string]int) // 要素の追加 scores["math"] = 90 scores["english"] = 85 fmt.Println(scores) // 出力例: map[english:85 math:90] // 要素の更新 scores["math"] = 95 fmt.Println(scores) // 出力例: map[english:85 math:95]
}
2.3. 要素の取得
マップから値を取得するには、マップ変数[キー]
という構文を使います。
package main
import "fmt"
func main() { scores := map[string]int{ "math": 95, "english": 85, } mathScore := scores["math"] fmt.Println("Math Score:", mathScore) // 出力: Math Score: 95 // 存在しないキーを指定した場合 scienceScore := scores["science"] fmt.Println("Science Score:", scienceScore) // 出力: Science Score: 0 (int型のゼロ値)
}
存在しないキーを指定して値を取得しようとすると、エラーにはならず、その値型のゼロ値(intなら0, stringなら””, boolならfalseなど)が返ります。
2.4. 要素の存在確認 (Comma ok イディオム)
キーが存在しない場合にゼロ値が返るだけでは、本当に値がゼロなのか、キーが存在しないのか区別できません。そこで、キーの存在を確認するために “comma ok” イディオム を使います。
マップの要素アクセスは、2つの戻り値を返すことができます。
value, ok := mapVariable[key]
value
: キーに対応する値 (存在しない場合はゼロ値)ok
: キーが存在すればtrue
、存在しなければfalse
となるbool値
package main
import "fmt"
func main() { scores := map[string]int{ "math": 95, "english": 0, // 意図的に0点を設定 } // "english" の存在確認 englishScore, ok := scores["english"] if ok { fmt.Printf("English score is %d (Key exists)\n", englishScore) } else { fmt.Println("English score not found") } // 出力: English score is 0 (Key exists) // "science" の存在確認 scienceScore, ok := scores["science"] if ok { fmt.Printf("Science score is %d (Key exists)\n", scienceScore) } else { fmt.Println("Science score not found") // こちらが出力される } // 出力: Science score not found // 値は不要で、存在確認だけしたい場合 _, exists := scores["math"] if exists { fmt.Println("Math key exists") } // 出力: Math key exists
}
2.5. 要素の削除
マップから要素を削除するには、組み込み関数の delete
を使います。
delete(マップ変数, キー)
指定したキーが存在しない場合でも、delete
関数はエラーを起こしません。
package main
import "fmt"
func main() { scores := map[string]int{ "math": 95, "english": 85, "science": 70, } fmt.Println("Before delete:", scores) // 出力例: Before delete: map[english:85 math:95 science:70] // "english"を削除 delete(scores, "english") fmt.Println("After delete 'english':", scores) // 出力例: After delete 'english': map[math:95 science:70] // 存在しないキーを削除してもエラーにならない delete(scores, "history") fmt.Println("After delete 'history':", scores) // 出力例: After delete 'history': map[math:95 science:70]
}
2.6. マップの要素数
マップに含まれる要素の数(キーと値のペアの数)を取得するには、組み込み関数の len
を使います。
package main
import "fmt"
func main() { scores := map[string]int{ "math": 95, "science": 70, } fmt.Println("Number of elements:", len(scores)) // 出力: Number of elements: 2
}
3. rangeによるマップの反復処理
マップのすべての要素(キーと値のペア)に対して処理を行いたい場合、for
ループと range
キーワードを組み合わせます。
3.1. キーと値を取得する
package main
import "fmt"
func main() { colors := map[string]string{ "red": "#FF0000", "green": "#00FF00", "blue": "#0000FF", } fmt.Println("--- Key and Value ---") for key, value := range colors { fmt.Printf("Key: %s, Value: %s\n", key, value) } // 出力例 (順序は実行ごとに変わる可能性があります): // Key: blue, Value: #0000FF // Key: red, Value: #FF0000 // Key: green, Value: #00FF00
}
3.2. キーのみを取得する
値を使わない場合は、値を受け取る変数を省略できます。
package main
import "fmt"
func main() { colors := map[string]string{ "red": "#FF0000", "green": "#00FF00", "blue": "#0000FF", } fmt.Println("\n--- Only Keys ---") for key := range colors { fmt.Printf("Key: %s\n", key) } // 出力例 (順序は実行ごとに変わる可能性があります): // Key: red // Key: green // Key: blue
}
3.3. 値のみを取得する
キーを使わない場合は、ブランク識別子 _
を使ってキーを無視します。
package main
import "fmt"
func main() { colors := map[string]string{ "red": "#FF0000", "green": "#00FF00", "blue": "#0000FF", } fmt.Println("\n--- Only Values ---") for _, value := range colors { fmt.Printf("Value: %s\n", value) } // 出力例 (順序は実行ごとに変わる可能性があります): // Value: #00FF00 // Value: #0000FF // Value: #FF0000
}
マップを
range
で反復処理する際、要素が取り出される順序は保証されません。プログラムを実行するたびに順序が変わる可能性があります。特定の順序で処理したい場合は、キーをスライスに格納し、そのスライスをソートしてからループ処理を行うなどの工夫が必要です。 package main
import ( "fmt" "sort"
)
func main() { scores := map[string]int{ "math": 95, "english": 85, "science": 70, "art": 90, } // 1. キーをスライスに格納 var keys []string for k := range scores { keys = append(keys, k) } // 2. キーのスライスをソート (文字列として) sort.Strings(keys) fmt.Println("--- Sorted by Key ---") // 3. ソートされたキーの順序でマップにアクセス for _, key := range keys { fmt.Printf("Key: %s, Value: %d\n", key, scores[key]) } // 出力 (常にこの順序): // Key: art, Value: 90 // Key: english, Value: 85 // Key: math, Value: 95 // Key: science, Value: 70
}
4. マップの活用例
マップは様々な場面で役立ちます。簡単な例をいくつか見てみましょう。
4.1. 単語の出現回数カウント
package main
import ( "fmt" "strings"
)
func main() { text := "apple banana apple orange banana apple" words := strings.Fields(text) // スペースで単語に分割 wordCounts := make(map[string]int) for _, word := range words { // カンマokイディオムで存在確認しつつカウントアップ // _, ok := wordCounts[word] // if ok { // wordCounts[word]++ // } else { // wordCounts[word] = 1 // } // ↑は以下のようにシンプルに書ける wordCounts[word]++ // キーが存在しなければゼロ値(0)に+1され、存在すれば現在の値に+1される } fmt.Println(wordCounts) // 出力例: map[apple:3 banana:2 orange:1]
}
4.2. 設定情報の管理
package main
import "fmt"
func main() { config := map[string]string{ "api_key": "your_secret_key", "endpoint": "https://api.example.com", "timeout_ms": "5000", } endpoint, ok := config["endpoint"] if ok { fmt.Println("API Endpoint:", endpoint) } else { fmt.Println("Endpoint not configured") } // 出力: API Endpoint: https://api.example.com
}
5. まとめ
今回はGo言語のマップ(map)と、その要素を反復処理するための range
の使い方について学びました。
- マップはキーと値のペアを格納するデータ構造です。
make
関数やマップリテラルで初期化します。マップ[キー]
で値にアクセスし、delete
で要素を削除します。- キーの存在確認には “comma ok” イディオム を使いましょう。
for key, value := range マップ
で要素を反復処理できます。range
での反復順序は保証されない 点に注意が必要です。
マップはGoプログラミングにおいて非常に頻繁に使われる重要な機能です。基本的な操作と注意点をしっかり押さえて、様々な場面で活用していきましょう!
次は Step 3 に進み、「関数定義と引数・戻り値」について学びます。お楽しみに!