Go 速習チュートリアル

Go 言語のマップ (Maps)

1. マップの概要

マップ(Maps)は、データを key:value(キー:値) のペアで格納するために使用されます。

  • 各要素は「キー:値」のペアで構成されます。
  • マップは順序が保証されず(unordered)、変更可能(changeable)なコレクションです。また、重複したキーを保持することはできません。
  • マップの長さは要素の数であり、len() 関数を使用して取得できます。
  • マップのデフォルト値(初期値)は nil です。
  • マップは内部的なハッシュテーブルへのリファレンス(参照)を保持します。

Goでは、マップを作成するための複数の方法が用意されています。

2. var と := を使用したマップの作成

2.1. シンタックス

var a = map[KeyType]ValueType{key1:value1, key2:value2,...}
b := map[KeyType]ValueType{key1:value1, key2:value2,...}

2.2. 実装例

この例では、Goでのマップ作成方法を示します。コード内の記述順と、出力結果の順序が異なる点に注目してください。

package main
import ("fmt")

func main() {
  var a = map[string]string{"brand": "Ford", "model": "Mustang", "year": "1964"}
  b := map[string]int{"Oslo": 1, "Bergen": 2, "Trondheim": 3, "Stavanger": 4}

  fmt.Printf("a\t%v\n", a)
  fmt.Printf("b\t%v\n", b)
}

実行結果:

a   map[brand:Ford model:Mustang year:1964]
b   map[Bergen:2 Oslo:1 Stavanger:4 Trondheim:3]

       注意: コード内で定義された要素の順序と、実際に格納される順序は異なります。データは、マップからの効率的な取得を最適化する形で格納されます。

3. make() 関数による作成

3.1. シンタックス

var a = make(map[KeyType]ValueType)
b := make(map[KeyType]ValueType)

3.2. 実装例

package main
import ("fmt")

func main() {
  var a = make(map[string]string) // マップは現在空です
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"
                                 // a は空ではなくなりました
  b := make(map[string]int)
  b["Oslo"] = 1
  b["Bergen"] = 2
  b["Trondheim"] = 3
  b["Stavanger"] = 4

  fmt.Printf("a\t%v\n", a)
  fmt.Printf("b\t%v\n", b)
}

4. 空のマップの作成

空のマップを作成するには2つの方法があります。一つは make() 関数を使用する方法、もう一つは以下のシンタックスを使用する方法です。

4.1. シンタックス

var a map[KeyType]ValueType

       重要: 空のマップを作成する正しい方法は make() 関数 を使うことです。それ以外の方法(リテラルなしの宣言のみ)で作成した空のマップに書き込みを行おうとすると、ランタイムパニックが発生します。

4.2. 宣言の違いの確認

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  var b map[string]string

  fmt.Println(a == nil) // 結果: false
  fmt.Println(b == nil) // 結果: true
}

5. キーと値に許可される型

5.1. キーに使用できる型

マップのキーには、比較演算子(==)が定義されている任意のデータ型を使用できます。

  • ブーリアン(Booleans)
  • 数値(Numbers)
  • 文字列(Strings)
  • 配列(Arrays)
  • ポインタ(Pointers)
  • 構造体(Structs)
  • インターフェース(Interface:動的な型が比較をサポートしている場合)

5.2. 無効なキーの型

比較演算子が定義されていない以下の型はキーとして使用できません。

  • スライス(Slices)
  • マップ(Maps)
  • 関数(Functions)

5.3. 値に使用できる型

値(Values)にはあらゆる型を使用可能です。

6. 要素へのアクセス

特定のキーを指定して値を取得します。

6.1. シンタックス

value = map_name[key]

6.2. 実装例

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"

  fmt.Printf(a["brand"])
}

7. 要素の更新と追加

要素の更新と追加は、同じシンタックスで行われます。

7.1. シンタックス

map_name[key] = value

7.2. 実装例

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"

  fmt.Println(a)

  a["year"] = "1970"  // 要素の更新
  a["color"] = "red"  // 要素の追加

  fmt.Println(a)
}

8. 要素の削除

要素の削除には組み込みの delete() 関数を使用します。

8.1. シンタックス

delete(map_name, key)

8.2. 実装例

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"

  fmt.Println(a)

  delete(a, "year")

  fmt.Println(a)
}

9. キーの存在確認

マップに特定のキーが存在するかどうかを確認するには、以下の書式を使用します。

9.1. シンタックス

val, ok := map_name[key]

存在確認だけが必要な場合は、値(val)の代わりにブランク識別子(_)を使用できます。

9.2. 実装例

package main
import ("fmt")

func main() {
  var a = map[string]string{"brand": "Ford", "model": "Mustang", "year": "1964", "day":""}

  val1, ok1 := a["brand"] // 存在するキーとその値を確認
  val2, ok2 := a["color"] // 存在しないキーを確認
  val3, ok3 := a["day"]   // 値が空文字列だが存在するキーを確認
  _, ok4 := a["model"]    // 値は不要で、存在のみを確認

  fmt.Println(val1, ok1)
  fmt.Println(val2, ok2)
  fmt.Println(val3, ok3)
  fmt.Println(ok4)
}

解説:
キー "color" は存在しないため、val2 は空文字列、ok2false になります。一方で "day" は値が空文字列ですが、キー自体は存在するため ok3true になります。

10. マップは参照型 (References)

マップはハッシュテーブルへのリファレンス(参照)です。
もし2つのマップ変数が同じハッシュテーブルを参照している場合、一方の内容を変更すると、もう一方の内容も影響を受けます。

10.1. 実装例

package main
import ("fmt")

func main() {
  var a = map[string]string{"brand": "Ford", "model": "Mustang", "year": "1964"}
  b := a

  fmt.Println(a)
  fmt.Println(b)

  b["year"] = "1970"
  fmt.Println("b を変更した後:")

  fmt.Println(a)
  fmt.Println(b)
}

11. マップの反復処理 (Iterate)

マップの全要素を走査するには range を使用します。

11.1. 実装例

package main
import ("fmt")

func main() {
  a := map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}

  for k, v := range a {
    fmt.Printf("%v : %v, ", k, v)
  }
}

12. 特定の順序での反復処理

マップは順序を持たないデータ構造です。もし特定の順序で反復処理を行いたい場合は、その順序を指定するための別のデータ構造(スライスなど)を用意する必要があります。

12.1. 実装例

package main
import ("fmt")

func main() {
  a := map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}

  var b []string             // 順序を定義するためのスライス
  b = append(b, "one", "two", "three", "four")

  for k, v := range a {        // 順序が保証されないループ
    fmt.Printf("%v : %v, ", k, v)
  }

  fmt.Println()

  for _, element := range b {  // 定義した順序に従ったループ
    fmt.Printf("%v : %v, ", element, a[element])
  }
}