キーワード

Go言語におけるmapの基本操作と応用例について解説

Goのmapはキーと値をペアで扱い、効率的なデータ管理ができる機能です。

この記事では、mapの宣言方法や基本操作を実例とともに紹介し、シンプルに使いこなすための手順を説明します。

Go言語mapの基本操作と初期例

宣言と初期化

var宣言とmakeによる初期化

Go言語では、まずvar宣言でmap型を宣言し、その後make関数を使ってmapを生成する方法があります。

下記のサンプルコードでは、myMapというmapをmakeで初期化し、キーとして文字列、値として整数を扱っています。

package main
import (
	"fmt"
)
func main() {
	// mapの宣言(キー:string, 値:int)
	var myMap map[string]int
	// makeを用いてmapを生成
	myMap = make(map[string]int)
	// 値の設定
	myMap["apple"] = 10    // キー"apple"に値10を設定
	myMap["banana"] = 20   // キー"banana"に値20を設定
	fmt.Println("初期化されたmap:", myMap)
}
初期化されたmap: map[apple:10 banana:20]

リテラルを用いた初期化方法

リテラルを使ってmapを初期化することで、宣言と同時に初期値を設定できます。

以下は、リテラルを用いた簡単な初期化例です。

package main
import (
	"fmt"
)
func main() {
	// リテラルを用いてmapを初期化
	myMap := map[string]int{
		"orange": 15,   // キー"orange"に値15を設定
		"grape":  25,   // キー"grape"に値25を設定
	}
	fmt.Println("リテラルで初期化されたmap:", myMap)
}
リテラルで初期化されたmap: map[grape:25 orange:15]

要素の追加・更新・削除

要素の追加および更新方法

mapでは、既存のキーに対して新しい値を設定することで更新され、存在しないキーに値を設定することで要素が追加されます。

次のサンプルコードでは、要素の追加と更新の方法を示しています。

package main
import (
	"fmt"
)
func main() {
	// リテラルでmapを初期化
	myMap := map[string]int{
		"key1": 100,
	}
	// 新しい要素の追加
	myMap["key2"] = 200  // キー"key2"を追加
	// 既存のキーの値を更新
	myMap["key1"] = 150  // キー"key1"の値を更新
	fmt.Println("更新後のmap:", myMap)
}
更新後のmap: map[key1:150 key2:200]

要素の削除と存在確認の手法

mapの要素を削除する場合は、delete関数を使用します。

また、特定のキーが存在するかどうかは、2つの戻り値を利用して確認できます。

以下のコードはその手法を示しています。

package main
import (
	"fmt"
)
func main() {
	// リテラルを用いてmapを初期化
	cityPopulation := map[string]int{
		"Tokyo":    13929286,
		"Osaka":    2691000,
		"Nagoya":   2305000,
	}
	// キー"Osaka"の存在確認と削除
	if value, exists := cityPopulation["Osaka"]; exists {
		fmt.Println("Osakaの人口は", value)
		// キー"Osaka"を削除する
		delete(cityPopulation, "Osaka")
	} else {
		fmt.Println("Osakaは存在しません")
	}
	// 削除後のmapを表示
	fmt.Println("削除後のcityPopulation:", cityPopulation)
}
Osakaの人口は 2691000
削除後のcityPopulation: map[Nagoya:2305000 Tokyo:13929286]

Go言語mapの走査と応用例

rangeによるmapの走査

キーと値の取得方法

rangeキーワードを使うと、mapの全てのキーと値にアクセスできます。

サンプルコードは、各要素のキーと値をループ処理を通して表示する方法を示しています。

package main
import (
	"fmt"
)
func main() {
	// サンプルmapの初期化
	fruitPrices := map[string]int{
		"apple":  120,
		"banana": 80,
		"cherry": 300,
	}
	// rangeによるmapの走査
	for key, value := range fruitPrices {
		fmt.Printf("キー: %s, 値: %d\n", key, value)
	}
}
キー: apple, 値: 120
キー: banana, 値: 80
キー: cherry, 値: 300

反復処理の具体例

mapの全要素に対して、特定の処理(例えば全ての値に対して計算を行うなど)を行う場合、rangeを活用できます。

下記のサンプルコードでは、mapの値に10%の増加を適用する例を示します。

package main
import (
	"fmt"
)
func main() {
	// サンプルmapの初期化(商品の単価とする)
	itemPrice := map[string]int{
		"pen":    100,
		"notebook": 200,
		"eraser":  50,
	}
	// 各商品の価格に対し、10%の増加を計算し表示
	for item, price := range itemPrice {
		// 新しい価格の計算(整数計算のため四捨五入は省略)
		newPrice := int(float64(price) * 1.1)
		fmt.Printf("%sの新価格: %d\n", item, newPrice)
	}
}
penの新価格: 110
notebookの新価格: 220
eraserの新価格: 55

キーの抽出とソート

キーのリスト化と並び替え

mapは順序が保証されないため、キーを並び替えて処理したい場合、まずキーを抽出し、標準パッケージのsortを用いて並び替える必要があります。

以下のサンプルコードでは、mapからキーを取り出し、アルファベット順にソートする方法を示しています。

package main
import (
	"fmt"
	"sort"
)
func main() {
	// サンプルmapの初期化
	countryCode := map[string]int{
		"Japan":    81,
		"USA":      1,
		"Germany":  49,
		"France":   33,
	}
	// キーの抽出
	var keys []string
	for key := range countryCode {
		keys = append(keys, key)
	}
	// キーのアルファベット順ソート
	sort.Strings(keys)
	fmt.Println("ソートされたキー:", keys)
}
ソートされたキー: [France Germany Japan USA]

ソート結果を活かした応用例

ソート済みのキーを利用して、map内の要素を指定された順序で操作または表示することができます。

下記の例では、ソートされたキー順にmapの内容を表示する方法を示しています。

package main
import (
	"fmt"
	"sort"
)
func main() {
	// サンプルmapの初期化(都市の人口)
	cityPopulation := map[string]int{
		"Tokyo":    13929286,
		"Osaka":    2691000,
		"Nagoya":   2305000,
	}
	// キーの抽出とソート
	var cities []string
	for city := range cityPopulation {
		cities = append(cities, city)
	}
	sort.Strings(cities)
	// ソート済み順にmapの内容を表示
	for _, city := range cities {
		fmt.Printf("都市: %s, 人口: %d\n", city, cityPopulation[city])
	}
}
都市: Nagoya, 人口: 2305000
都市: Osaka, 人口: 2691000
都市: Tokyo, 人口: 13929286

Go言語mapのパフォーマンスと並行処理上の注意点

パフォーマンスの基本知識

mapは高速な検索と挿入が可能なデータ構造で、キーから値へ迅速にアクセスできます。

ただし、mapの内部実装ではハッシュテーブルが用いられており、最悪計算量がO(n)になる可能性があるため、特に大規模なデータの場合には注意が必要です。

また、mapのサイズ取得にはlen関数を使いますが、これは定数時間で実行されます。

並行アクセスにおける注意点

Goのmapは、複数のゴルーチンから同時にアクセス(読み書き)されると、競合状態が発生しプログラムがパニックに陥る可能性があります。

並行処理環境下でmapを扱う場合は、排他制御を行うか、sync.Mapを利用するのが一般的です。

sync.Mapを用いた安全な並行アクセス対応

sync.Mapは、ロック機構を内部で持ち、複数のゴルーチンから安全にアクセスできるmapです。

下記のサンプルコードは、sync.Mapを用いて並行処理でmapにアクセスする方法を示しています。

package main
import (
	"fmt"
	"sync"
)
func main() {
	// sync.Mapの生成
	var safeMap sync.Map
	// 複数のゴルーチンでの並行アクセス(書き込み)
	var wg sync.WaitGroup
	wg.Add(2)
	// ゴルーチン1
	go func() {
		defer wg.Done()
		// キー"key1"に値を設定
		safeMap.Store("key1", "値1")
	}()
	// ゴルーチン2
	go func() {
		defer wg.Done()
		// キー"key2"に値を設定
		safeMap.Store("key2", "値2")
	}()
	// ゴルーチンの終了を待機
	wg.Wait()
	// safeMapの走査
	safeMap.Range(func(key, value interface{}) bool {
		fmt.Printf("キー: %v, 値: %v\n", key, value)
		return true  // 全ての値を走査するためtrueを返す
	})
}
キー: key1, 値: 値1
キー: key2, 値: 値2

まとめ

この記事では、Go言語のmapに関する基本操作や初期化方法、要素の追加・更新・削除、走査、並行処理での注意点を具体例とともに解説しました。

mapの宣言、初期化、要素操作、走査とソートのテクニック、sync.Mapを利用した安全な並行アクセス対応について理解することができました。

ぜひ、自身のプロジェクトで実際に試して、効率的なプログラミングを体験してみてください。

関連記事

Back to top button
目次へ