20
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

Goで多次元マップ(複数のキーからなるマップ)を実現したいときにはどうするか

Goのマップは内部的には1つのポインタのようなものである。ポインタの値そのものがnilのマップと、実体がメモリ上に割り当てられているがその中身が空のマップとは、似て非なるものである。nilマップを読もうとするとそれは空のマップのように振る舞うが、空のマップとは異なりnilマップには値をセットすることはできない。

var m1 map[string]int      // m1はnilマップ
m2 := make(map[string]int) // m2は空のマップ

Goのマップでは、存在しないキーを参照したときには値のゼロ値(デフォルトの値)が返ってくることになっている。たとえば下のようなマップで存在しないキーを参照すると0が返ってくる。0はint型のゼロ値だ。

m := make(map[string]int)  // キーがstring、値がintのマップ
i := m["nosuchkey"]        // iは0

マップのゼロ値はnilマップだ。だからマップのマップをアクセスしたとき、キーが存在しないと、nilマップが返ってくることになる。

var m map[string]map[string]int

// vはmap[string]int型のnilマップ
v := m["foo"]

// wはmap[string]int型のnilマップにアクセスした結果
// (値のゼロ値 -- つまりこの場合int型の0)
w := m["foo"]["bar"]

さて、map[string]map[string]intは多次元マップだったわけだけど、値を読むぶんにはだいたい期待通りに動いていると言っていいと思う。しかし値をセットしようと思うとこの多次元マップは期待通りにはうまく動いてくれない。どうしてかというと、nilマップには(空のマップとは異なり)値をセットするすることができないからだ。

var m map[string]map[string]int
// m["foo"]がnilなのでnilマップにセットできずパニックする
m["foo"]["bar"] = 42

この制限を回避するためにはかなり回りくどい書き方をしなければいけない。nilマップにセットしようとするとエラーになるわけだから、親のマップのキーが存在しているかどうかをまずチェックして、存在していなければ空のマップを割り当ててそれをそのキーにセットすればよい。とはいえどうみてもこの書き方は面倒だ。

m := make(map[string]map[string]int)
if _, exist := m["foo"]; !exist {
    m["foo"] = make(map[string]int)
}
m["foo"]["bar"] = 42

ではどうすればいいかというと、多次元マップの代わりに1次元マップを使えばよい。複数のキーをまとめた構造体を作ってそれを1次元マップのキーにすることによって、実質的に多次元マップのようなものを単純なマップで実現することができる。コードで表すとこういうようになる。

type key struct {
    k1, k2 string
}
m := make(map[key]int)
m[key{"foo", "bar"}] = 42

多次元マップのキーが1つの構造体に圧縮されて1次元になっているのがわかると思う。このテクニックを使うと多次元マップと同等の機能を簡単に実現することができる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
20
Help us understand the problem. What are the problem?