基于map实现sets.go
package sets
// An Empty struct does not take up any memory
type Empty struct{}
// sets.String is a set of strings, implemented via map[string]struct{} for minimal memory consumption.
type String map[string]Empty
func NewString(items ...string) String {
s := String{}
s.Insert(items...)
return s
}
func (s String) Insert(items ...string) {
for _, item := range items {
s[item] = Empty{}
}
}
func (s String) Delete(items ...string) {
for _, item := range items {
delete(s, item)
}
}
func (s String) Has(item string) bool {
_, res := s[item]
return res
}
// HasAll returns true if and only if all items are contained in the set.
func (s String) HasAll(items ...string) bool {
for _, item := range items {
if !s.Has(item) {
return false
}
}
return true
}
// HasAny returns true if any items are contained in the set.
func (s String) HasAny(items ...string) bool {
for _, item := range items {
if s.Has(item) {
return true
}
}
return false
}
// Difference returns a set of objects that are not in s2
func (s String) Difference(s2 String) String {
res := NewString()
for item := range s {
if !s2.Has(item) {
res.Insert(item)
}
}
return res
}
// Union returns a new set which includes items in either s1 or s2.
func (s1 String) Union(s2 String) String {
res := NewString()
for item := range s1 {
res.Insert(item)
}
for item := range s2 {
res.Insert(item)
}
return res
}
// Intersection returns a new set which includes the item in BOTH s1 and s2
func (s1 String) Intersection(s2 String) String {
res := NewString()
if s1.Len() < s2.Len() {
for item := range s1 {
if s2.Has(item) {
res.Insert(item)
}
}
} else {
for item := range s2 {
if s1.Has(item) {
res.Insert(item)
}
}
}
return res
}
func (s String) Len() int {
return len(s)
}
func (s String) List() []string {
res := make([]string, 0, len(s))
for item := range s {
res = append(res, item)
}
return res
}
单元测试 sets_test.go
package sets
import (
"testing"
)
func TestInsert(t *testing.T) {
ss := NewString()
// Insert 1 item\
ss.Insert("a")
if !ss.Has("a") {
t.Errorf("Insert failed.")
}
// Insert several items
ss.Insert("a", "bb", "ccc", "dddd")
if !ss.HasAll("a", "bb", "ccc", "dddd") || ss.Len() != 4 {
t.Errorf("Insert failed.")
}
}
func TestDelete(t *testing.T) {
ss := NewString("a", "b", "c", "d")
// Delete 1 item
ss.Delete("a")
if ss.Has("a") {
t.Errorf("Delete failed.")
}
// Delete several items
ss.Delete("b", "c", "d")
if ss.HasAny("b", "c", "d") || ss.Len() != 0 {
t.Errorf("Delete failed.")
}
}
func TestHas(t *testing.T) {
s := "abc"
ss := NewString(s)
_, res1 := ss[s]
res2 := ss.Has(s)
if (res1 && !res2) || (!res1 && res2) {
t.Errorf("Has Check failed.")
}
}
func TestHasAny(t *testing.T) {
ss := NewString("a", "b", "c", "d")
_, resA := ss["a"]
_, resB := ss["b"]
res1 := resA || resB
res2 := ss.HasAny("a", "b")
if (res1 && !res2) || (!res1 && res2) {
t.Errorf("HasAny Check failed.")
}
}
func TestHasAll(t *testing.T) {
ss := NewString("a", "b", "c")
_, resA := ss["a"]
_, resB := ss["b"]
_, resC := ss["c"]
res1 := resA && resB && resC
res2 := ss.HasAll("a", "b", "c")
if (res1 && !res2) || (!res1 && res2) {
t.Errorf("HasAll Check failed.")
}
}
func TestDifference(t *testing.T) {
ss1 := NewString("a", "b")
ss2 := NewString("a", "c")
res := ss1.Difference(ss2)
if !res.Has("b") {
t.Errorf("Difference Check failed.")
}
}
func TestUnion(t *testing.T) {
ss1 := NewString("a")
ss2 := NewString("b")
res := ss1.Union(ss2)
if !res.HasAll("a", "b") {
t.Errorf("Union Check failed.")
}
}
func TestIntersection(t *testing.T) {
ss1 := NewString("a", "b")
ss2 := NewString("a", "c")
res := ss1.Intersection(ss2)
if !res.Has("a") {
t.Errorf("Intersection Check failed.")
}
}
func TestLen(t *testing.T) {
ss1 := NewString("a", "b")
if ss1.Len() != 2 {
t.Errorf("Len Check failed.")
}
}