Go言語用の汎用的なHashSet実装です。
Goでは標準ライブラリとしてHashSetは提供されていませんが、map[T]struct{}で同じことが出来るので基本的にそれで代用することが多いです。が、毎回同じ処理を書いているのでついでに簡易ライブラリにしたという経緯です。
go get github.com/devlights/hashset@latestpackage main
import (
"fmt"
"github.com/devlights/hashset"
)
func main() {
// 新しい文字列集合を作成
fruits := hashset.New[string]()
// 要素を追加
fruits.Add("apple")
fruits.Add("banana")
fruits.Add("orange")
// 要素の存在を確認
fmt.Println(fruits.Contains("apple")) // true
fmt.Println(fruits.Contains("grape")) // false
// サイズを取得
fmt.Printf("集合のサイズ: %d\n", fruits.Size()) // 集合のサイズ: 3
// スライスに変換
fruitsSlice := fruits.ToSlice()
fmt.Printf("フルーツ: %v\n", fruitsSlice)
}// 空の集合を作成
set := hashset.New[string]()
// 初期容量を指定して作成(パフォーマンス最適化のため)
set := hashset.NewWithCapacity[string](100)
// スライスから作成
items := []string{"apple", "banana", "orange"}
set := hashset.NewFromSlice(items)// 単一要素を追加
set.Add("apple")
// 複数要素を追加
set.AddAll([]string{"banana", "orange"})
// 単一要素を削除
set.Remove("apple")
// 複数要素を削除
set.RemoveAll([]string{"banana", "orange"})
// 存在確認
exists := set.Contains("apple")
// 複数要素の確認
allExist := set.ContainsAll([]string{"apple", "banana"})
anyExist := set.ContainsAny([]string{"apple", "grape"})
// サイズ取得と空かどうかの確認
size := set.Size()
empty := set.IsEmpty()
// すべての要素をクリア
set.Clear()// スライスに変換
slice := set.ToSlice()
// コピーを作成
clone := set.Clone()set1 := hashset.NewFromSlice([]string{"apple", "banana"})
set2 := hashset.NewFromSlice([]string{"banana", "orange"})
// 和集合 (A ∪ B)
union := set1.Union(set2) // {"apple", "banana", "orange"}
// 積集合 (A ∩ B)
intersection := set1.Intersection(set2) // {"banana"}
// 差集合 (A - B)
difference := set1.Difference(set2) // {"apple"}
// 対称差集合 ((A ∪ B) - (A ∩ B))
symDiff := set1.SymmetricDifference(set2) // {"apple", "orange"}set1 := hashset.NewFromSlice([]string{"apple", "banana"})
set2 := hashset.NewFromSlice([]string{"apple", "banana", "orange"})
// set1がset2の部分集合かどうかを確認
isSubset := set1.IsSubsetOf(set2) // true
// set2がset1の上位集合かどうかを確認
isSuperset := set2.IsSupersetOf(set1) // true
// 集合が共通要素を持たないかどうかを確認
disjoint := set1.IsDisjoint(hashset.NewFromSlice([]string{"grape", "kiwi"})) // true
// 集合が等しいかどうかを確認
equal := set1.Equal(set1.Clone()) // true// 要素を反復処理
for item := range set.All() {
fmt.Println(item)
}// 文字列集合
fruits := hashset.NewFromSlice([]string{"apple", "banana", "orange"})
// 整数集合
numbers := hashset.NewFromSlice([]int{1, 2, 3, 4, 5})
// カスタム構造体(比較可能である必要があります)
type Person struct {
Name string
Age int
}
people := hashset.New[Person]()
people.Add(Person{Name: "Alice", Age: 30})
people.Add(Person{Name: "Bob", Age: 25})users1 := hashset.NewFromSlice([]string{"alice", "bob", "charlie"})
users2 := hashset.NewFromSlice([]string{"bob", "david", "eve"})
users3 := hashset.NewFromSlice([]string{"bob", "frank", "grace"})
// 3つすべての集合に共通するユーザーを検索
common := users1.Intersection(users2).Intersection(users3)
fmt.Println("共通ユーザー:", common.ToSlice()) // [bob]func removeDuplicates[T comparable](slice []T) []T {
set := hashset.NewFromSlice(slice)
return set.ToSlice()
}
// 使用例
numbers := []int{1, 2, 2, 3, 3, 3, 4, 5}
unique := removeDuplicates(numbers)
// unique には [1, 2, 3, 4, 5] が含まれます(順序は保証されません)// ユーザー権限の処理
adminUsers := hashset.NewFromSlice([]string{"alice", "bob"})
activeUsers := hashset.NewFromSlice([]string{"alice", "charlie", "david"})
bannedUsers := hashset.NewFromSlice([]string{"eve", "frank"})
// アクティブな管理者ユーザーを検索
activeAdmins := adminUsers.Intersection(activeUsers)
// システムにアクセス可能なユーザーを検索(アクティブでかつ禁止されていない)
allowedUsers := activeUsers.Difference(bannedUsers)
// いずれかのカテゴリに言及されているすべてのユーザーを検索
allUsers := adminUsers.Union(activeUsers).Union(bannedUsers)この実装はスレッドセーフではありません。並行アクセスが必要な場合は、適切な同期処理でラップしてください:
import "sync"
type SafeSet[T comparable] struct {
set hashset.Set[T]
mu sync.RWMutex
}
func NewSafeSet[T comparable]() *SafeSet[T] {
return &SafeSet[T]{
set: hashset.New[T](),
}
}
func (s *SafeSet[T]) Add(item T) {
s.mu.Lock()
defer s.mu.Unlock()
s.set.Add(item)
}
func (s *SafeSet[T]) Contains(item T) bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.set.Contains(item)
}