Go言語学習その20~マップ

今回は他の言語でもよく使うマップについて見てみます。


■マップ型の変数宣言いろいろ

マップは、キーとそれに対するバリューのコレクションで、
JavaでいうHashMapに該当するものです。

配列は
a[0] = 1
a[1] = 2
のように、インデックス(数値)に対してバリューを設定するものですが

マップは
m["one"] = 1
m["two"] = 2
のように、
配列でのインデックス部分に数値以外のものが指定できるようになる
イメージですね。

Goでのマップ型の変数宣言にはいくつかのパターンがあります。

package main

import (
	"fmt"
)

func main() {

	// varを使ったmap変数宣言1
	var m1 map[string]int
	
	if m1 == nil {
		fmt.Println("マップ m1はnilです")
	}

	// panicが発生します
	//m1["one"] = 1

	// varを使ったmap変数宣言2
	var m2 = map[string]int{}

	if m2 != nil {
		fmt.Println("マップ m2はnilではありません")
	}

	m2["one"] = 1

	fmt.Println("マップ m2の値", m2)
	
	// varを使わないmap変数宣言
	m3 := map[string]int{}

	m3["one"] = 1

	fmt.Println("マップ m3の値", m3)

	// map変数宣言時に初期値設定
	m4 := map[string]int{
		"one":1,
		"two":2,
		"three":3,
	}

	fmt.Println("マップ m4の値", m4)

	// makeを使ってmap生成
	m5 := make(map[string]int)

	m5["one"] = 1

	fmt.Println("マップ m5の値", m5)

	// makeを使ってmap生成:要素数指定
	m6 := make(map[string]int, 3)

	fmt.Printf("マップ m6:len=%d %v\n", len(m6), m6)

	m6["one"] = 1
	m6["two"] = 2
	m6["three"] = 3

	fmt.Printf("マップ m6:len=%d %v\n", len(m6), m6)

	m6["four"] = 4

	fmt.Printf("マップ m6:len=%d %v\n", len(m6), m6)
}

上記サンプルのコードを見ていきます。

・varを使ったmap変数宣言1
 マップの型指定は以下のようにします。
 map[キーの型]バリューの型
 
 上記サンプルでははじめに、以下の宣言を行っています。
 var m1 map[string]int
 
 キーがstring型、バリュ-がint型のmap型変数 m1 を宣言しています。
 この宣言の直後では、まだmap型変数 m1 に対してマップが
 割り当たっていません。
 マップが割り当たっていないmap型変数 m1 は、
 nil (Javaでのnullのようなもの) になります。
 
 上記サンプルの条件判定「if m1 == nil」部分はtrueとなります。
 
 また、nil状態の変数 m1 を使って
 m1["one"] = 1
 としてマップに値を格納しようとすると
 panic(いわゆるランタイムエラー)が発生します。

・varを使ったmap変数宣言2
 上記サンプルでは次に、以下の宣言を行っています。
 var m2 = map[string]int{}
 
 「map[string]int{}」で
 「キーがstring型、バリュ-がint型の空のマップ」が生成され、
 そして変数 m2 に割り当てられます。
 
 変数 m2 にマップが割り当たっているので
 上記サンプルの条件判定「if m2 != nil」部分はtrueとなります。
 
 また、変数 m2 を使って
 m2["one"] = 1
 を実行することで変数 m2 に割り当たっているマップに
 キー:one
 バリュー:1
 が格納されます。

・varを使わないmap変数宣言
 上記サンプルでは次に、以下の宣言を行っています。
 m3 := map[string]int{}
 varを使わない宣言も当然可能です。
 
・map変数宣言時に初期値設定
 上記サンプルでは次に、以下の宣言を行っています。
 m4 := map[string]int{
  "one":1,
  "two":2,
  "three":3,
 }
 
 変数宣言と同時に初期値設定するパターンですね。
 【キー】:【バリュー】
 という形式で初期値を設定します。
 
・makeを使ってmap生成
 上記サンプルでは次に、以下の宣言を行っています。
 m5 := make(map[string]int)
 
 make関数で空のマップを生成しています。
 m3 := map[string]int{}
 と同じ挙動になりますが、
 個人的には「{}」を付ける表記よりも
 make関数を使う表記の方が読みやすいのではないかな、と思います。

・makeを使ってmap生成:要素数指定
 上記サンプルでは次に、以下の宣言を行っています。
 m6 := make(map[string]int, 3)
 
 make関数でマップを生成していますが、
 第2引数に「3」を指定しています。
 これは、
 ・string型のキーとint型のバリューを格納するためのバッファを
  3要素分用意しておく
 という意味になります。
 
 この宣言直後のマップの長さは 0 であり空のマップになりますが
 メモリ上にバッファが3要素分存在しているので
 
 m6["one"] = 1
 m6["two"] = 2
 m6["three"] = 3
 と3要素分のキーとバリューを格納するときは
 メモリを保持する処理が無い分パフォーマンスが良くなります。
 
 make関数で要素数を 3 と指定していても
 4 つ目の要素を追加することは可能ですが
 m6["four"] = 4
 このときは、バッファが用意されてないので
 メモリを保持する処理
 が動くためパフォーマンスが落ちます。

■マップの使い方

生成されたマップに対する各種使い方を見てみます。

package main

import (
	"fmt"
)

func main() {

	m := make(map[string]int)

	// マップへの値の挿入・更新
	m["one"] = 1
	m["two"] = 2
	m["three"] = 3

	fmt.Println("マップ mの値", m)

	// バリューの取得
	value1 := m["two"]

	fmt.Println("マップのバリューの値", value1)

	// マップからキーを削除
	delete(m, "two")

	fmt.Println("マップ mの値", m)

	// キーの存在有無判定
	value2, containsKey := m["one"]
	
	fmt.Printf("マップのキー「one」のバリュー %v \n", value2)

	if containsKey {
		fmt.Println("キー「one」は存在します")
	} else {
		fmt.Println("キー「one」は存在しません")
	}

	value3, containsKey := m["two"]

	fmt.Printf("マップのキー「two」のバリュー %v \n", value3)

	if containsKey {
		fmt.Println("キー「two」は存在します")
	} else {
		fmt.Println("キー「two」は存在しません")
	}

}

・マップへの値の新規登録・更新
 マップに対するキー・バリューの新規登録、
 およびバリューの更新は以下のようにして行います。

 m["one"] = 1
 キー値「one」が存在しない場合は新規にマップに
 キー・バリューが登録されます。
 キー値「one」が存在する場合はバリューが「1」に更新されます。
 
・マップのバリューの取得
 マップからキー「two」に対するバリューを取得するには
 value := m["two"]
 とします。
 
・マップから要素を削除
 マップから要素を削除するには、delete関数を使います。
 delete(m, "two")
 キー値「two」を第2引数に指定することで
 キー値「two」とそれに対するバリュー値ともに削除されます。

・キーの存在有無判定
 マップにキーが存在するかどうかをチェックするには
 以下のようにします。
 value2, containsKey := m["one"]
 value2には、キー「one」に対するバリュー値が格納され、
 containsKeyには
 キー「one」がマップに存在したら true
 キー「one」がマップに存在しなければ false
 が格納されます。
 
 この containsKey を条件文で「if containsKey~」とすることで
 キーの存在有無に応じた処理を行うなどできます。
 
 ※ キーが存在しない場合のバリュー
   キーが存在しない場合、
   上記の value2 はバリューのデータ型の「ゼロ値」となります。
   バリューのデータ型がint型の場合は 0 となります。
   上記の value2 が 0 だった場合、
   「バリュー値が 0 だったのか」
   「キーが存在しないのか」
   が判断できないので
   上記の containsKey を使った判断が必要になります。

#プログラミング
#IT
#プログラミング言語
#Go言語
#GO
#Golang


この記事が気に入ったらサポートをしてみませんか?