【初心者でもわかる】Go言語のmap使い方ガイド|キーと値でデータをかんたん管理

Go

Go言語(Golang)でプログラムを書いていると、「名前と点数を組み合わせたい」「単語の出現回数を数えたい」など、データをキーと値のペアで管理したい場面がよくあります。

「スライスとなにが違うの?」
「どうやって使えばいいの?」
そんな疑問をかかえていませんか?

そんなときに便利なのがmap(マップ)です。

mapは、他の言語でいう辞書型や連想配列に相当し、データの検索や分類、頻度の計算などにとても便利です。

この記事では、Goのmapについて、定義・追加・更新・削除といった基本操作から、実用的な使い方まで、初心者にもわかりやすく解説します。

スポンサーリンク
  1. mapってなに?|データを「鍵と宝箱」で管理する便利な仕組み
    1. mapの基本的な考え方
    2. 身近な例で理解しよう
    3. mapが得意なこと
    4. スライスとの違い
  2. mapの作り方|定義から初期化まで3つの方法
    1. 基本的な宣言方法
    2. 方法1:make関数を使う初期化
    3. 方法2:リテラル(初期値付き)で初期化
    4. 方法3:空のmapリテラル
    5. よく使うキーと値の型の組み合わせ
  3. mapの基本操作|データの追加・更新・取得をマスターしよう
    1. データの追加と更新
    2. データの取得
    3. キーの存在確認(重要!)
    4. よく使うパターンの例
  4. データの削除とループ処理|mapの中身を自由に操作しよう
    1. データの削除
    2. すべてのデータを見るループ処理
    3. キーだけ、値だけを取得する方法
    4. 実用的なループの例
  5. 実用的な活用テクニック|mapでできる便利な処理
    1. 応用例1:文字の出現回数をカウント
    2. 応用例2:グループ分けして集計
    3. 応用例3:データの変換・マッピング
    4. 応用例4:複雑なデータ構造(mapの中にスライス)
  6. mapを使うときの注意点|知っておきたい重要なポイント
    1. 注意点1:キーに使える型の制限
    2. 注意点2:mapの初期化を忘れずに
    3. 注意点3:mapのコピーは「浅いコピー」
    4. 注意点4:mapの要素は順序が保証されない
    5. 注意点5:mapのゼロ値とnil
  7. よく使うmapのパターン集|実際の開発で役立つ書き方
    1. パターン1:設定値の管理
    2. パターン2:キャッシュの実装
    3. パターン3:エラーコードと説明の管理
    4. パターン4:グルーピングとフィルタリング
  8. まとめ|mapをマスターしてGo言語のデータ処理を効率化しよう
    1. 覚えておきたい重要なポイント
    2. よくある質問(FAQ)

mapってなに?|データを「鍵と宝箱」で管理する便利な仕組み

mapの基本的な考え方

mapは「キー(鍵)と値(データ)」のペアを保存するデータ構造です。

辞書で単語(キー)を引いて意味(値)を調べるのと同じような仕組みです。

身近な例で理解しよう

学生の成績管理

田中さん → 85点
佐藤さん → 92点
山田さん → 78点

商品の在庫管理

りんご → 50個
ばなな → 30個
みかん → 25個

mapが得意なこと

用途具体例メリット
データの分類カテゴリごとに商品をまとめる整理しやすい
頻度のカウント文字や単語の出現回数を数える自動で集計
IDと情報の紐づけ学籍番号と学生情報を関連付け高速で検索

スライスとの違い

特徴スライスmap
データの管理方法順番(インデックス)で管理キー(名前)で管理
アクセス方法slice[0], slice[1]map["田中"], map["佐藤"]
データの順番順番が決まっている順番は保証されない
適している用途リスト、配列的なデータ関連付けられたデータ

mapの役割は「関連付けられたデータの効率的な管理」です。次は、mapの定義方法と初期化の書き方を見てみましょう。

mapの作り方|定義から初期化まで3つの方法

基本的な宣言方法

var m map[string]int

この書き方では、まだ使える状態になっていません

使おうとするとエラー(panic)になってしまいます。

方法1:make関数を使う初期化

基本の書き方

m := make(map[string]int)

実際の使用例

package main

import "fmt"

func main() {
    // 空のmapを作る
    scores := make(map[string]int)
    
    // データを追加
    scores["田中"] = 85
    scores["佐藤"] = 92
    scores["山田"] = 78
    
    fmt.Println(scores) // map[佐藤:92 田中:85 山田:78]
}

方法2:リテラル(初期値付き)で初期化

基本の書き方

m := map[string]string{
    "apple":  "りんご",
    "banana": "ばなな",
    "orange": "みかん",
}

実際の使用例

package main

import "fmt"

func main() {
    // 最初からデータを入れて作る
    fruits := map[string]string{
        "apple":  "りんご",
        "banana": "ばなな",
        "orange": "みかん",
    }
    
    fmt.Println(fruits["apple"]) // りんご
}

方法3:空のmapリテラル

基本の書き方

m := map[string]int{}

これはmake(map[string]int)と同じ効果があります。

よく使うキーと値の型の組み合わせ

キーの型値の型使用例
stringint名前と点数、単語と出現回数
stringstring英語と日本語、IDと名前
intstring番号と名前、IDと情報
string[]stringカテゴリと商品リスト
stringbool名前と合格/不合格

makeやリテラルで初期化することで、mapをすぐに使える状態にできます。次は、mapへのデータ追加・更新・取得方法を見てみましょう。

mapの基本操作|データの追加・更新・取得をマスターしよう

データの追加と更新

基本の書き方

m["キー"] = 値

実際の例

package main

import "fmt"

func main() {
    scores := make(map[string]int)
    
    // データを追加
    scores["田中"] = 85
    scores["佐藤"] = 92
    scores["山田"] = 78
    
    // データを更新(同じキーに新しい値)
    scores["田中"] = 90
    
    fmt.Println(scores) // map[佐藤:92 田中:90 山田:78]
}

データの取得

基本の取得方法

値 := m["キー"]

実際の例

package main

import "fmt"

func main() {
    fruits := map[string]string{
        "apple":  "りんご",
        "banana": "ばなな",
    }
    
    // 値を取得
    appleName := fruits["apple"]
    fmt.Println(appleName) // りんご
    
    // 存在しないキーを取得すると「ゼロ値」が返る
    orangeName := fruits["orange"]
    fmt.Println(orangeName) // (空文字)
}

キーの存在確認(重要!)

基本の書き方

値, 存在するか := m["キー"]

実際の例

package main

import "fmt"

func main() {
    scores := map[string]int{
        "田中": 85,
        "佐藤": 92,
    }
    
    // キーが存在する場合
    score, ok := scores["田中"]
    if ok {
        fmt.Printf("田中さんの点数は %d 点です\n", score)
    } else {
        fmt.Println("田中さんのデータがありません")
    }
    
    // キーが存在しない場合
    score, ok = scores["山田"]
    if ok {
        fmt.Printf("山田さんの点数は %d 点です\n", score)
    } else {
        fmt.Println("山田さんのデータがありません")
    }
}

出力結果

田中さんの点数は 85 点です
山田さんのデータがありません

よく使うパターンの例

パターン1:存在チェック付きの処理

if score, exists := scores["田中"]; exists {
    fmt.Printf("田中さん: %d点\n", score)
} else {
    fmt.Println("田中さんのデータが見つかりません")
}

パターン2:デフォルト値を設定

score := scores["田中"]
if score == 0 {
    score = 50 // デフォルト値
}
fmt.Printf("点数: %d\n", score)

mapは、存在チェックもかんたんにできるため、予期しないエラーを防げます。次は、要素の削除やループ処理を見てみましょう。

データの削除とループ処理|mapの中身を自由に操作しよう

データの削除

基本の書き方

delete(map名, "削除したいキー")

実際の例

package main

import "fmt"

func main() {
    fruits := map[string]string{
        "apple":  "りんご",
        "banana": "ばなな",
        "orange": "みかん",
    }
    
    fmt.Println("削除前:", fruits)
    
    // "banana"を削除
    delete(fruits, "banana")
    
    fmt.Println("削除後:", fruits)
    
    // 存在しないキーを削除してもエラーにならない
    delete(fruits, "grape") // エラーなし
}

出力結果

削除前: map[apple:りんご banana:ばなな orange:みかん]
削除後: map[apple:りんご orange:みかん]

すべてのデータを見るループ処理

基本の書き方

for キー, 値 := range map名 {
    // 処理
}

実際の例

package main

import "fmt"

func main() {
    scores := map[string]int{
        "田中": 85,
        "佐藤": 92,
        "山田": 78,
        "鈴木": 88,
    }
    
    // すべての成績を表示
    fmt.Println("=== 成績一覧 ===")
    for name, score := range scores {
        fmt.Printf("%s: %d点\n", name, score)
    }
}

出力結果の例

=== 成績一覧 ===
田中: 85点
佐藤: 92点
山田: 78点
鈴木: 88点

重要な注意点:順番は保証されない mapの要素をrangeで取得する順番は、実行するたびに変わる可能性があります。決まった順番が必要な場合は、別途ソートが必要です。

キーだけ、値だけを取得する方法

キーだけ取得

for key := range scores {
    fmt.Println("名前:", key)
}

値だけ取得

for _, score := range scores {
    fmt.Println("点数:", score)
}

実用的なループの例

例1:合格者を調べる

package main

import "fmt"

func main() {
    scores := map[string]int{
        "田中": 85,
        "佐藤": 92,
        "山田": 78,
        "鈴木": 88,
    }
    
    fmt.Println("=== 合格者一覧(80点以上)===")
    for name, score := range scores {
        if score >= 80 {
            fmt.Printf("%s: %d点 ✓\n", name, score)
        }
    }
}

例2:平均点を計算

package main

import "fmt"

func main() {
    scores := map[string]int{
        "田中": 85,
        "佐藤": 92,
        "山田": 78,
        "鈴木": 88,
    }
    
    total := 0
    count := 0
    
    for _, score := range scores {
        total += score
        count++
    }
    
    average := float64(total) / float64(count)
    fmt.Printf("平均点: %.1f点\n", average)
}

deleteやrangeを使えば、mapの中身を自在に操作できます。

次は、mapの応用テクニックや注意点を見てみましょう。

実用的な活用テクニック|mapでできる便利な処理

応用例1:文字の出現回数をカウント

何をするプログラム? 文字列の中で、それぞれの文字が何回出てくるかを数えます。

package main

import "fmt"

func main() {
    text := "gopher"
    
    // 文字ごとの出現回数を記録するmap
    charCount := make(map[rune]int)
    
    // 文字列を1文字ずつチェック
    for _, char := range text {
        charCount[char]++ // その文字の回数を1増やす
    }
    
    // 結果を表示
    fmt.Println("=== 文字の出現回数 ===")
    for char, count := range charCount {
        fmt.Printf("'%c': %d回\n", char, count)
    }
}

出力結果

=== 文字の出現回数 ===
'g': 1回
'o': 1回
'p': 1回
'h': 1回
'e': 1回
'r': 1回

応用例2:グループ分けして集計

何をするプログラム? 学生を学年ごとに分けて、それぞれの学年に何人いるかを数えます。

package main

import "fmt"

func main() {
    // 学生のデータ(名前と学年)
    students := map[string]int{
        "田中": 1,
        "佐藤": 2,
        "山田": 1,
        "鈴木": 3,
        "高橋": 2,
        "渡辺": 1,
    }
    
    // 学年ごとの人数を記録するmap
    gradeCount := make(map[int]int)
    
    // 各学生の学年を確認して人数を数える
    for _, grade := range students {
        gradeCount[grade]++
    }
    
    // 結果を表示
    fmt.Println("=== 学年別人数 ===")
    for grade, count := range gradeCount {
        fmt.Printf("%d年生: %d人\n", grade, count)
    }
}

出力結果

=== 学年別人数 ===
1年生: 3人
2年生: 2人
3年生: 1人

応用例3:データの変換・マッピング

何をするプログラム? 英語の月名を日本語に変換します。

package main

import "fmt"

func main() {
    // 英語から日本語への変換表
    monthMap := map[string]string{
        "January":   "1月",
        "February":  "2月",
        "March":     "3月",
        "April":     "4月",
        "May":       "5月",
        "June":      "6月",
        "July":      "7月",
        "August":    "8月",
        "September": "9月",
        "October":   "10月",
        "November":  "11月",
        "December":  "12月",
    }
    
    englishMonths := []string{"March", "June", "September"}
    
    fmt.Println("=== 月名の変換 ===")
    for _, month := range englishMonths {
        if japanese, exists := monthMap[month]; exists {
            fmt.Printf("%s → %s\n", month, japanese)
        } else {
            fmt.Printf("%s → 変換できません\n", month)
        }
    }
}

応用例4:複雑なデータ構造(mapの中にスライス)

何をするプログラム? 各チームのメンバー一覧を管理します。

package main

import "fmt"

func main() {
    // チーム名をキーにして、メンバーのスライスを値にする
    teams := map[string][]string{
        "開発チーム": {"田中", "佐藤", "山田"},
        "営業チーム": {"鈴木", "高橋"},
        "企画チーム": {"渡辺", "伊藤", "加藤", "小林"},
    }
    
    fmt.Println("=== チーム構成 ===")
    for teamName, members := range teams {
        fmt.Printf("\n【%s】(%d人)\n", teamName, len(members))
        for i, member := range members {
            fmt.Printf("  %d. %s\n", i+1, member)
        }
    }
}

出力結果

=== チーム構成 ===

【開発チーム】(3人)
  1. 田中
  2. 佐藤
  3. 山田

【営業チーム】(2人)
  1. 鈴木
  2. 高橋

【企画チーム】(4人)
  1. 渡辺
  2. 伊藤
  3. 加藤
  4. 小林

これらの例を見ると、mapがとても柔軟で強力なデータ構造であることがわかります。

次は、mapを使うときの注意点を確認しましょう。

mapを使うときの注意点|知っておきたい重要なポイント

注意点1:キーに使える型の制限

使えるキーの型

  • 基本型:int, string, bool, float64など
  • 配列:[3]intなど(要素数が決まっているもの)
  • 構造体:比較可能なフィールドのみ

使えないキーの型

  • スライス:[]int, []stringなど
  • map:map[string]intなど
  • 関数:func()など
// ❌ これはエラーになる
// var badMap map[[]string]int

// ✅ これは正しい
var goodMap map[string]int
var arrayKeyMap map[[3]int]string // 配列はOK

注意点2:mapの初期化を忘れずに

ダメな例(panic になる)

var m map[string]int
m["test"] = 1 // panic: assignment to entry in nil map

正しい例

m := make(map[string]int)
m["test"] = 1 // OK

注意点3:mapのコピーは「浅いコピー」

package main

import "fmt"

func main() {
    original := map[string]int{
        "apple": 1,
        "banana": 2,
    }
    
    // これは「浅いコピー」- 同じデータを指している
    copied := original
    
    // copiedを変更すると、originalも変わる
    copied["orange"] = 3
    
    fmt.Println("original:", original) // map[apple:1 banana:2 orange:3]
    fmt.Println("copied:", copied)     // map[apple:1 banana:2 orange:3]
}

深いコピーをしたい場合

func deepCopyMap(original map[string]int) map[string]int {
    copy := make(map[string]int)
    for key, value := range original {
        copy[key] = value
    }
    return copy
}

注意点4:mapの要素は順序が保証されない

package main

import "fmt"

func main() {
    m := map[string]int{
        "a": 1,
        "b": 2,
        "c": 3,
    }
    
    // 実行するたびに順番が変わる可能性がある
    for key, value := range m {
        fmt.Printf("%s: %d\n", key, value)
    }
}

順序が必要な場合の解決方法

package main

import (
    "fmt"
    "sort"
)

func main() {
    m := map[string]int{
        "banana": 2,
        "apple":  1,
        "cherry": 3,
    }
    
    // キーを取得してソート
    keys := make([]string, 0, len(m))
    for key := range m {
        keys = append(keys, key)
    }
    sort.Strings(keys)
    
    // ソートされた順序で表示
    for _, key := range keys {
        fmt.Printf("%s: %d\n", key, m[key])
    }
}

注意点5:mapのゼロ値とnil

package main

import "fmt"

func main() {
    var m map[string]int
    
    // 値の読み取りは可能(ゼロ値が返る)
    value := m["test"]
    fmt.Println(value) // 0
    
    // 存在チェックも可能
    _, exists := m["test"]
    fmt.Println(exists) // false
    
    // でも書き込みはpanic
    // m["test"] = 1 // panic!
}

よく使うmapのパターン集|実際の開発で役立つ書き方

パターン1:設定値の管理

package main

import "fmt"

func main() {
    config := map[string]interface{}{
        "port":        8080,
        "host":        "localhost",
        "debug":       true,
        "maxUsers":    1000,
        "appName":     "MyApp",
    }
    
    // 型アサーションで使用
    if port, ok := config["port"].(int); ok {
        fmt.Printf("サーバーポート: %d\n", port)
    }
    
    if appName, ok := config["appName"].(string); ok {
        fmt.Printf("アプリ名: %s\n", appName)
    }
}

パターン2:キャッシュの実装

package main

import "fmt"

// 簡単なキャッシュの例
var cache = make(map[string]string)

func expensiveOperation(input string) string {
    // キャッシュをチェック
    if result, exists := cache[input]; exists {
        fmt.Printf("キャッシュから取得: %s\n", input)
        return result
    }
    
    // 重い処理をシミュレート
    fmt.Printf("計算中: %s\n", input)
    result := "処理済み_" + input
    
    // 結果をキャッシュに保存
    cache[input] = result
    return result
}

func main() {
    // 最初の呼び出し(計算する)
    fmt.Println(expensiveOperation("データA"))
    
    // 2回目の呼び出し(キャッシュから取得)
    fmt.Println(expensiveOperation("データA"))
    
    // 別のデータ(計算する)
    fmt.Println(expensiveOperation("データB"))
}

パターン3:エラーコードと説明の管理

package main

import "fmt"

func main() {
    errorMessages := map[int]string{
        404: "ページが見つかりません",
        500: "サーバーエラーが発生しました",
        403: "アクセスが禁止されています",
        401: "認証が必要です",
    }
    
    errorCodes := []int{200, 404, 500, 403}
    
    for _, code := range errorCodes {
        if message, exists := errorMessages[code]; exists {
            fmt.Printf("エラー %d: %s\n", code, message)
        } else {
            fmt.Printf("エラー %d: 不明なエラーです\n", code)
        }
    }
}

パターン4:グルーピングとフィルタリング

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    people := []Person{
        {"田中", 25, "東京"},
        {"佐藤", 30, "大阪"},
        {"山田", 22, "東京"},
        {"鈴木", 28, "名古屋"},
        {"高橋", 35, "大阪"},
    }
    
    // 都市ごとにグループ化
    cityGroups := make(map[string][]Person)
    
    for _, person := range people {
        cityGroups[person.City] = append(cityGroups[person.City], person)
    }
    
    // 結果を表示
    for city, group := range cityGroups {
        fmt.Printf("\n=== %s在住 (%d人) ===\n", city, len(group))
        for _, person := range group {
            fmt.Printf("  %s (%d歳)\n", person.Name, person.Age)
        }
    }
}

まとめ|mapをマスターしてGo言語のデータ処理を効率化しよう

覚えておきたい重要なポイント

mapの基本

  1. 定義と初期化make(map[string]int)またはmap[string]int{}
  2. データ操作:追加・更新・取得・削除の基本操作
  3. 存在チェックvalue, ok := map[key]パターンの活用
  4. ループ処理for key, value := range mapでの全要素処理

実用的な活用方法

  • 頻度のカウント(文字数、単語数など)
  • データの分類とグループ化
  • 設定値やエラーメッセージの管理
  • キャッシュの実装

注意すべきポイント

  • 初期化を忘れない(panic防止)
  • キーの型制限を理解する
  • 順序が保証されないことを理解する
  • 浅いコピーの性質を理解する

よくある質問(FAQ)

Q: mapとスライスはどう使い分けるべきですか?

A: 順序が重要で連続したデータはスライス、キーで検索したいデータはmapを使いましょう。

Q: mapのキーに構造体は使えますか?

A: 比較可能なフィールドのみの構造体なら使えます。スライスやmapをフィールドに持つ構造体は使えません。

Q: mapの要素数を調べるにはどうすればいいですか?

A: len(map名)で要素数を取得できます。

Q: mapをからっぽにするにはどうすればいいですか?

A: 新しいmapを作り直すか、rangeで全要素をdeleteします。

コメント

タイトルとURLをコピーしました