bauer's diary

凡人の凡人による凡人のための備忘録

GoでSet型を実現する場合の選択肢

はじめに

Go言語には標準で Set 型がありません。
ここでは2通りの実現方法を検討してみます。

1. map ✕ structで実現する

mapのフィールドに空のstructを使ってsetを定義します。
structは何もフィールドを持たない場合、サイズは0になるのでコストがかかりません。

A struct{} takes up no space.

golang-nutsにて説明されています。

package main

import . "fmt"

func main() {
	list := []string{
		"test1",
		"test2",
		"test3",
	}

	set := make(map[string]struct{})
	for _, v := range list {
		set[v] = struct{}{}
	}

	Printf("%#v\n", set)
}
map[string]struct {}{
  "test1":struct {}{},
  "test2":struct {}{},
  "test3":struct {}{}
}


2. ライブラリを使う

golang-setというライブラリがいけてそうなので紹介します。
2017/02/03にもコミットがあり、わりと最近も更新されているようです。
github.com

GoDocはこちら


関数はかなり充実していました。

package main

import (
	. "fmt"

	"github.com/deckarep/golang-set"
)

func main() {
	s1 := mapset.NewSet()
	s1.Add("1")
	s1.Add("2")
	s1.Add(3)
	s1.Add(4)
	print(s1) // Set{4, "1", "2", 3}

	// slice → set
	slice := []interface{}{"1", "2", 3, "5"}
	s2 := mapset.NewSetFromSlice(slice)
	print(s2) // Set{"5", "1", "2", 3}

	// set同士の結合
	all := s1.Union(s2)
	print(all)                // Set{3, 4, "1", "2", "5"}
	print(all.Difference(s1)) // Set{"5"}

	// 要素数確認
	Println(s1.Cardinality())  // 4
	Println(s2.Cardinality())  // 4
	Println(all.Cardinality()) // 5

	// 存在チェック
	Println(s1.Contains(1))   // false
	Println(s1.Contains("1")) // true

	// 要素削除
	s1.Remove(4)
	print(s1) // Set{"1", "2", 3}

	// 要素全削除
	s1.Clear()
	print(s1) // Set{}
}

func print(s mapset.Set) {
	Printf("%#v\n", s)
}


まとめ

簡易的でよければ 1 で、
凝った使い方をしたければ 2 という選択がよさそうです。


おしまい。