package main
import (
"fmt"
"slices"
)
func unique[T comparable](in []T) []T {
seen := make(map[T]struct{}, len(in))
out := make([]T, 0, len(in))
for _, v := range in {
if _, ok := seen[v]; ok {
continue
}
seen[v] = struct{}{}
out = append(out, v)
}
return out
}
func main() {
fmt.Println(unique([]string{"b", "a", "b", "c", "a"}))
fmt.Println(unique([]int{3, 1, 3, 3, 2}))
// If order does not matter: sort, then drop adjacent duplicates.
s := []int{3, 1, 3, 3, 2}
slices.Sort(s)
fmt.Println(slices.Compact(s))
}
package main
import (
"fmt"
"maps"
"slices"
)
type set map[string]struct{}
func of(items ...string) set {
s := make(set, len(items))
for _, it := range items {
s[it] = struct{}{}
}
return s
}
func union(a, b set) set {
out := maps.Clone(a)
for k := range b {
out[k] = struct{}{}
}
return out
}
func intersect(a, b set) set {
if len(a) > len(b) {
a, b = b, a
}
out := set{}
for k := range a {
if _, ok := b[k]; ok {
out[k] = struct{}{}
}
}
return out
}
func difference(a, b set) set {
out := set{}
for k := range a {
if _, ok := b[k]; !ok {
out[k] = struct{}{}
}
}
return out
}
func sorted(s set) []string {
return slices.Sorted(maps.Keys(s))
}
func main() {
backend := of("go", "sql", "docker", "linux")
devops := of("docker", "linux", "terraform")
fmt.Println("union: ", sorted(union(backend, devops)))
fmt.Println("intersect: ", sorted(intersect(backend, devops)))
fmt.Println("backend-only:", sorted(difference(backend, devops)))
}
sorted 只是为了让输出稳定。通过 range 遍历来打印集合,每次运行的顺序都不同。maps.Keys 和 slices.Sorted 需要 Go 1.23。
像 type set map[string]struct{} 这样的命名类型仍然是 map:你可以用同样的方式对它索引、range 遍历和 delete,还可以给它定义方法。
一个小巧的泛型 Set
有了泛型(Go 1.18),一个类型就能覆盖所有可比较的元素类型。对大多数程序来说,这已经足够了:
package main
import (
"cmp"
"fmt"
"maps"
"slices"
)
type Set[T comparable] struct {
m map[T]struct{}
}
func NewSet[T comparable](items ...T) *Set[T] {
s := &Set[T]{m: make(map[T]struct{}, len(items))}
for _, it := range items {
s.Add(it)
}
return s
}
func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Remove(v T) { delete(s.m, v) }
func (s *Set[T]) Len() int { return len(s.m) }
func (s *Set[T]) Has(v T) bool {
_, ok := s.m[v]
return ok
}
func (s *Set[T]) Union(o *Set[T]) *Set[T] {
out := &Set[T]{m: maps.Clone(s.m)}
for v := range o.m {
out.Add(v)
}
return out
}
// Sorted is a free function because it needs a stricter constraint than the type.
func Sorted[T cmp.Ordered](s *Set[T]) []T {
return slices.Sorted(maps.Keys(s.m))
}
func main() {
a := NewSet(1, 2, 3)
b := NewSet(3, 4)
a.Add(2)
fmt.Println(a.Len(), a.Has(2), a.Has(9))
u := a.Union(b)
fmt.Println(Sorted(u))
words := NewSet("go", "rust")
words.Remove("rust")
fmt.Println(Sorted(words))
}