[Goのはじめ方] Part9: マップ(map)とrangeの使い方

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 // これはランタイムパニックを引き起こす!
}
注意: 宣言だけして初期化されていないマップ (nil マップ) に要素を追加しようとすると、ランタイムパニックが発生します。必ず `make` やリテラルで初期化してから使いましょう。

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 の順序
マップを 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 に進み、「関数定義と引数・戻り値」について学びます。お楽しみに!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です