見出し画像

Go言語ではset型がないのでmapを使おう

Go言語を業務で使い始めて2ヶ月ほどが経ちました。

業務の中で知らなかったせいで恥をかいてしまったGo言語特有の変わった実装がありました。反省の意味も込めて紹介したいと思います。

(noteの記事でコードブロックを使ったことがなかったので、一回試しに使ってみたいという裏テーマもあります。 )

Go言語には set型 がない!

Go言語には struct や interface、mapなどの型が事前に用意されているものの、実は set型が用意されていません。

ここでいうset型とは、数学で言う集合のことで、重複しない要素が集まったものを表現したい時に用います。Pythonには組込型として提供されていて、以下のように簡単に使うことができます。

myset = set()
myset.add(1)
myset.add(2)
myset.add(2)

print(myset) 
# Output: {1, 2} 

Go言語でのmapとstruct 

Go言語では連想配列型を定義するためのmapがあります。

map[int64]string

が表すのは、int64をキー、stringを値とする連想配列の型です。

また、Go言語にはstruct{}で独自の型を定義できます。

type MyStruct struct {
    Name    string
    Age     int64
}

この例だと、MyStruct型 は、stringとint64を属性として持つことができる構造体の型です。

Go言語で set型(みたいなもの)を使いたい

さて、本題のset型は次のように実装するらしいです(stringをキーとする時)。

type MySet map[string]struct{}

なにこれ?

stringをキー、struct{}型を値とする連想配列の型set型と同じように使えるらしいのですが、どういう意味でしょうか。

この型にキーと値を設定する場合、値は必ず struct{}{} です。

mySet := make(map[string]struct{})
mySet["hoge"] = struct{}{}
mySet["geho"] = struct{}{}
mySet["geho"] = struct{}{}

ここで代入したキーの一覧はまさに欲しかった重複のない要素の集合になるのです。

for val := range mySet {
    fmt.Println(val)
}

// Output:
// hoge
// geho

map型が持つ性質である「キーに対してただ一つの値が定まる」ことを活かして、set型としての要素の一意性を実現しているわけです。

つまり、値の型はぶっちゃけなんでも良いことになってしまいますが、struct{}型を用いるメリットは、値のサイズが0バイトになり、最も効率が良いかららしいです。

「キーが存在しているかどうか=set型に要素が含まれているか」になります。

if _, ok := mySet["someKey"]; ok {
    fmt.Println("someKey exists in mySet")
} else {
    fmt.Println("someKey doesn't exist in mySet")
}

英語ですが、以下の記事が用例がたくさんあり参考になります。

まとめ

map[<type of key>]struct{} がset型として機能することがわかりました。

奇妙な見た目をしていますが、余計な機能を極力省き、必要最小限の機能を組み合わせて応用していくまさにGo wayを感じるお話でした。

(裏テーマ感想)

noteでコードブロックを使ってみたけど、言語を選択できないのでシンタックスハイライトが神頼みならぬnote頼みであるのが困り物。



よろしければサポートお願いします!本の購入などに充てます!