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 に進み、「関数定義と引数・戻り値」について学びます。お楽しみに!
コメント