演習 - マップを使用する

完了

Go のマップは基本的にハッシュ テーブルであり、キーと値のペアのコレクションです。 マップ内のすべてのキーは同じ型である必要があり、値も同様です。 ただし、キーと値には異なる型を使用できます。 たとえば、キーは数値で、値は文字列にすることができます。 マップ内の特定の項目にアクセスするには、そのキーを参照します。

マップを宣言して初期化する

マップを宣言するには、map キーワードを使用する必要があります。 次に map[T]T のようにキーと値の型を定義します。 たとえば、学生の年齢を含むマップを作成する場合は、次のコードを使用できます。

package main

import "fmt"

func main() {
    studentsAge := map[string]int{
        "john": 32,
        "bob":  31,
    }
    fmt.Println(studentsAge)
}

上記のコードを実行すると、次の出力が表示されます。

map[bob:31 john:32]

マップを項目で初期化しない場合は、組み込みの make() 関数を使って前のセクションのマップを作成できます。 次のコードを使用すると、空のマップを作成できます。

studentsAge := make(map[string]int)

マップは動的です。 作成後に、項目の追加、アクセス、または削除を行うことができます。 これらのアクションを見てみましょう。

項目の追加

項目を追加するには、スライスのときのように組み込み関数を使用する必要はありません。 マップはより直感的です。 必要なのは、キーと値を定義することだけです。 ペアが存在しない場合は、項目がマップに追加されます。

前に使用したコードを書き直し、make 関数を使用してマップを作成してみましょう。 その後、マップに項目を追加します。 次のコードを使用できます。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

このコードを実行すると、前と同じ出力が表示されます。

map[bob:31 john:32]

初期化したマップに項目が追加されたことに注目してください。 ただし、nil マップで同じ操作を実行しようとすると、エラーが表示されます。 たとえば、次のコードは機能しません。

package main

import "fmt"

func main() {
    var studentsAge map[string]int
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

前述のコードを実行すると、次のエラーが表示されます。

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /Users/johndoe/go/src/helloworld/main.go:7 +0x4f
exit status 2

マップに項目を追加するときの問題を回避するには、前のコード スニペットに示したように、make 関数を使用して空のマップ (nil マップではなく) を作成するようにします。 このルールは、項目を追加する場合にのみ適用されます。 nil マップで検索、削除、またはループの操作を実行した場合、Go がパニックに陥ることはありません。 この動作についてはすぐに確認します。

項目にアクセスする

マップ内の項目にアクセスするには、配列やスライスの場合と同様に、通常の添字表記 m[key] を使用します。 項目にアクセスする方法の簡単な例を次に示します。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println("Bob's age is", studentsAge["bob"])
}

マップで添字表記を使用すると、マップにキーが存在しない場合でも、常に応答が返されます。 存在しない項目にアクセスしても、Go はパニックになりません。 代わりに、既定値が返されます。 次のコードを使用して、この動作を確認できます。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println("Christy's age is", studentsAge["christy"])
}

上記のコードを実行すると、次の出力が表示されます。

Christy's age is 0

多くの場合、マップ内に存在しない項目にアクセスしても Go によってエラーが返されないのはフェアな動作です。 ただし、項目が存在するかどうかを知る必要がある場合があります。 Go では、マップの添字表記で 2 つの値を生成できます。 1 つ目は項目の値です。 2 つ目は、キーが存在するかどうかを示すブール値のフラグです。

最後のコード スニペットの問題を解決するには、次のコードを使用できます。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    age, exist := studentsAge["christy"]
    if exist {
        fmt.Println("Christy's age is", age)
    } else {
        fmt.Println("Christy's age couldn't be found")
    }
}

上記のコードを実行すると、次の出力が表示されます。

Christy's age couldn't be found

アクセスする前にマップにキーが存在するかどうかを確認するには、2 番目のコード スニペットを使用します。

項目を削除する

マップから項目を削除するには、組み込みの delete() 関数を使用します。 マップから項目を削除する方法の例を次に示します。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    delete(studentsAge, "john")
    fmt.Println(studentsAge)
}

このコードを実行すると、次の出力が表示されます。

map[bob:31]

既に説明したように、存在しない項目を削除しようとしても、Go がパニックに陥ることはありません。 その動作の例を次に示します。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    delete(studentsAge, "christy")
    fmt.Println(studentsAge)
}

このコードを実行すると、エラーは表示されず、次の出力が表示されます。

map[bob:31 john:32]

マップ内でループさせる

最後に、マップ内でループさせて、プログラムですべての項目にアクセスできるようにする方法を見てみましょう。 これを行うには、次の例のように範囲ベースのループを使用できます。

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    for name, age := range studentsAge {
        fmt.Printf("%s\t%d\n", name, age)
    }
}

上記のコードを実行すると、次の出力が表示されます。

john    32
bob     31

キーと値の情報を異なる変数に格納する方法に注目してください。 この例では、キーを name 変数に保持し、値を age 変数に保持しています。 そのため、range によってまず項目のキーが生成され、次にその項目の値が生成されます。 次の例のように _ 変数を使用すると、それらのいずれかを無視できます。

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    for _, age := range studentsAge {
        fmt.Printf("Ages %d\n", age)
    }
}

この場合は、age をこのように出力しても意味がありませんが、項目のキーを知る必要がない場合があります。 また、次の例のように項目のキーのみを使用することができます。

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    for name := range studentsAge {
        fmt.Printf("Names %s\n", name)
    }
}