分享
  1. 首页
  2. 文章

golang练手小项目系列(6)-使用map实现set

李说的对 · · 938 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

问题描述

go没有提供set数据结构,请用map实现set

要点

需要支持方法:

  • Add 添加元素
  • Remove 删除元素
  • Cardinality 获取 Set 长度
  • Clear 清空 Set
  • Contains 检测元素是否在 Set 中
  • Pop() 随机删除一个元素并返回被删除的元素
  • ToSlice() []interface{} 转换成slice返回

拓展

  • Clone 复制 Set
  • Difference(other Set) Set 返回和另一个Set的差集
  • Equal(other Set) bool 判断和另一个Set是否相等
  • Intersect(other Set) Set 返回和另一个Set的交集
  • SymmetricDifference(other Set) Set 返回不在交集中的元素的集合
  • Union(other Set) Set 返回并集
  • 实现一个线程安全的版本

实现


//set.go
// Package mapset implements a simple and generic set collection.
// Items stored within it are unordered and unique. It supports
// typical set operations: membership testing, intersection, union,
// difference, symmetric difference and cloning.
//
// Package mapset provides two implementations of the Set
// interface. The default implementation is safe for concurrent
// access, but a non-thread-safe implementation is also provided for
// programs that can benefit from the slight speed improvement and
// that can enforce mutual exclusion through other means.
package mapset
// Set is the primary interface provided by the mapset package. It
// represents an unordered set of data and a large number of
// operations that can be applied to that set.
type Set interface {
 // Adds an element to the set. Returns whether
 // the item was added.
 Add(i interface{}) bool
 // Returns the number of elements in the set.
 Cardinality() int
 // Removes all elements from the set, leaving
 // the empty set.
 Clear()
 // Returns a clone of the set using the same
 // implementation, duplicating all keys.
 Clone() Set
 // Returns whether the given items
 // are all in the set.
 Contains(i ...interface{}) bool
 // Returns the difference between this set
 // and other. The returned set will contain
 // all elements of this set that are not also
 // elements of other.
 //
 // Note that the argument to Difference
 // must be of the same type as the receiver
 // of the method. Otherwise, Difference will
 // panic.
 Difference(other Set) Set
 // Determines if two sets are equal to each
 // other. If they have the same cardinality
 // and contain the same elements, they are
 // considered equal. The order in which
 // the elements were added is irrelevant.
 //
 // Note that the argument to Equal must be
 // of the same type as the receiver of the
 // method. Otherwise, Equal will panic.
 Equal(other Set) bool
 // Returns a new set containing only the elements
 // that exist only in both sets.
 //
 // Note that the argument to Intersect
 // must be of the same type as the receiver
 // of the method. Otherwise, Intersect will
 // panic.
 Intersect(other Set) Set
 // Determines if every element in this set is in
 // the other set but the two sets are not equal.
 //
 // Note that the argument to IsProperSubset
 // must be of the same type as the receiver
 // of the method. Otherwise, IsProperSubset
 // will panic.
 IsProperSubset(other Set) bool
 // Determines if every element in the other set
 // is in this set but the two sets are not
 // equal.
 //
 // Note that the argument to IsSuperset
 // must be of the same type as the receiver
 // of the method. Otherwise, IsSuperset will
 // panic.
 IsProperSuperset(other Set) bool
 // Determines if every element in this set is in
 // the other set.
 //
 // Note that the argument to IsSubset
 // must be of the same type as the receiver
 // of the method. Otherwise, IsSubset will
 // panic.
 IsSubset(other Set) bool
 // Determines if every element in the other set
 // is in this set.
 //
 // Note that the argument to IsSuperset
 // must be of the same type as the receiver
 // of the method. Otherwise, IsSuperset will
 // panic.
 IsSuperset(other Set) bool
 // Iterates over elements and executes the passed func against each element.
 // If passed func returns true, stop iteration at the time.
 Each(func(interface{}) bool)
 // Returns a channel of elements that you can
 // range over.
 Iter() <-chan interface{}
 // Returns an Iterator object that you can
 // use to range over the set.
 Iterator() *Iterator
 // Remove a single element from the set.
 Remove(i interface{})
 // Provides a convenient string representation
 // of the current state of the set.
 String() string
 // Returns a new set with all elements which are
 // in either this set or the other set but not in both.
 //
 // Note that the argument to SymmetricDifference
 // must be of the same type as the receiver
 // of the method. Otherwise, SymmetricDifference
 // will panic.
 SymmetricDifference(other Set) Set
 // Returns a new set with all elements in both sets.
 //
 // Note that the argument to Union must be of the
 // same type as the receiver of the method.
 // Otherwise, IsSuperset will panic.
 Union(other Set) Set
 // Pop removes and returns an arbitrary item from the set.
 Pop() interface{}
 // Returns all subsets of a given set (Power Set).
 PowerSet() Set
 // Returns the Cartesian Product of two sets.
 CartesianProduct(other Set) Set
 // Returns the members of the set as a slice.
 ToSlice() []interface{}
}
// NewSet creates and returns a reference to an empty set. Operations
// on the resulting set are thread-safe.
func NewSet(s ...interface{}) Set {
 set := newThreadSafeSet()
 for _, item := range s {
 set.Add(item)
 }
 return &set
}
// NewSetWith creates and returns a new set with the given elements.
// Operations on the resulting set are thread-safe.
func NewSetWith(elts ...interface{}) Set {
 return NewSetFromSlice(elts)
}
// NewSetFromSlice creates and returns a reference to a set from an
// existing slice. Operations on the resulting set are thread-safe.
func NewSetFromSlice(s []interface{}) Set {
 a := NewSet(s...)
 return a
}
// NewThreadUnsafeSet creates and returns a reference to an empty set.
// Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSet() Set {
 set := newThreadUnsafeSet()
 return &set
}
// NewThreadUnsafeSetFromSlice creates and returns a reference to a
// set from an existing slice. Operations on the resulting set are
// not thread-safe.
func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
 a := NewThreadUnsafeSet()
 for _, item := range s {
 a.Add(item)
 }
 return a
}
// iterator.go
package mapset
// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's
// elements.
type Iterator struct {
 C <-chan interface{}
 stop chan struct{}
}
// Stop stops the Iterator, no further elements will be received on C, C will be closed.
func (i *Iterator) Stop() {
 // Allows for Stop() to be called multiple times
 // (close() panics when called on already closed channel)
 defer func() {
 recover()
 }()
 close(i.stop)
 // Exhaust any remaining elements.
 for range i.C {
 }
}
// newIterator returns a new Iterator instance together with its item and stop channels.
func newIterator() (*Iterator, chan<- interface{}, <-chan struct{}) {
 itemChan := make(chan interface{})
 stopChan := make(chan struct{})
 return &Iterator{
 C: itemChan,
 stop: stopChan,
 }, itemChan, stopChan
}
// threadunsafe.go
package mapset
import (
 "bytes"
 "encoding/json"
 "fmt"
 "reflect"
 "strings"
)
type threadUnsafeSet map[interface{}]struct{}
// An OrderedPair represents a 2-tuple of values.
type OrderedPair struct {
 First interface{}
 Second interface{}
}
func newThreadUnsafeSet() threadUnsafeSet {
 return make(threadUnsafeSet)
}
// Equal says whether two 2-tuples contain the same values in the same order.
func (pair *OrderedPair) Equal(other OrderedPair) bool {
 if pair.First == other.First &&
 pair.Second == other.Second {
 return true
 }
 return false
}
func (set *threadUnsafeSet) Add(i interface{}) bool {
 _, found := (*set)[i]
 if found {
 return false //False if it existed already
 }
 (*set)[i] = struct{}{}
 return true
}
func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
 for _, val := range i {
 if _, ok := (*set)[val]; !ok {
 return false
 }
 }
 return true
}
func (set *threadUnsafeSet) IsSubset(other Set) bool {
 _ = other.(*threadUnsafeSet)
 if set.Cardinality() > other.Cardinality() {
 return false
 }
 for elem := range *set {
 if !other.Contains(elem) {
 return false
 }
 }
 return true
}
func (set *threadUnsafeSet) IsProperSubset(other Set) bool {
 return set.IsSubset(other) && !set.Equal(other)
}
func (set *threadUnsafeSet) IsSuperset(other Set) bool {
 return other.IsSubset(set)
}
func (set *threadUnsafeSet) IsProperSuperset(other Set) bool {
 return set.IsSuperset(other) && !set.Equal(other)
}
func (set *threadUnsafeSet) Union(other Set) Set {
 o := other.(*threadUnsafeSet)
 unionedSet := newThreadUnsafeSet()
 for elem := range *set {
 unionedSet.Add(elem)
 }
 for elem := range *o {
 unionedSet.Add(elem)
 }
 return &unionedSet
}
func (set *threadUnsafeSet) Intersect(other Set) Set {
 o := other.(*threadUnsafeSet)
 intersection := newThreadUnsafeSet()
 // loop over smaller set
 if set.Cardinality() < other.Cardinality() {
 for elem := range *set {
 if other.Contains(elem) {
 intersection.Add(elem)
 }
 }
 } else {
 for elem := range *o {
 if set.Contains(elem) {
 intersection.Add(elem)
 }
 }
 }
 return &intersection
}
func (set *threadUnsafeSet) Difference(other Set) Set {
 _ = other.(*threadUnsafeSet)
 difference := newThreadUnsafeSet()
 for elem := range *set {
 if !other.Contains(elem) {
 difference.Add(elem)
 }
 }
 return &difference
}
func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
 _ = other.(*threadUnsafeSet)
 aDiff := set.Difference(other)
 bDiff := other.Difference(set)
 return aDiff.Union(bDiff)
}
func (set *threadUnsafeSet) Clear() {
 *set = newThreadUnsafeSet()
}
func (set *threadUnsafeSet) Remove(i interface{}) {
 delete(*set, i)
}
func (set *threadUnsafeSet) Cardinality() int {
 return len(*set)
}
func (set *threadUnsafeSet) Each(cb func(interface{}) bool) {
 for elem := range *set {
 if cb(elem) {
 break
 }
 }
}
func (set *threadUnsafeSet) Iter() <-chan interface{} {
 ch := make(chan interface{})
 go func() {
 for elem := range *set {
 ch <- elem
 }
 close(ch)
 }()
 return ch
}
func (set *threadUnsafeSet) Iterator() *Iterator {
 iterator, ch, stopCh := newIterator()
 go func() {
 L:
 for elem := range *set {
 select {
 case <-stopCh:
 break L
 case ch <- elem:
 }
 }
 close(ch)
 }()
 return iterator
}
func (set *threadUnsafeSet) Equal(other Set) bool {
 _ = other.(*threadUnsafeSet)
 if set.Cardinality() != other.Cardinality() {
 return false
 }
 for elem := range *set {
 if !other.Contains(elem) {
 return false
 }
 }
 return true
}
func (set *threadUnsafeSet) Clone() Set {
 clonedSet := newThreadUnsafeSet()
 for elem := range *set {
 clonedSet.Add(elem)
 }
 return &clonedSet
}
func (set *threadUnsafeSet) String() string {
 items := make([]string, 0, len(*set))
 for elem := range *set {
 items = append(items, fmt.Sprintf("%v", elem))
 }
 return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
}
// String outputs a 2-tuple in the form "(A, B)".
func (pair OrderedPair) String() string {
 return fmt.Sprintf("(%v, %v)", pair.First, pair.Second)
}
func (set *threadUnsafeSet) Pop() interface{} {
 for item := range *set {
 delete(*set, item)
 return item
 }
 return nil
}
func (set *threadUnsafeSet) PowerSet() Set {
 powSet := NewThreadUnsafeSet()
 nullset := newThreadUnsafeSet()
 powSet.Add(&nullset)
 for es := range *set {
 u := newThreadUnsafeSet()
 j := powSet.Iter()
 for er := range j {
 p := newThreadUnsafeSet()
 if reflect.TypeOf(er).Name() == "" {
 k := er.(*threadUnsafeSet)
 for ek := range *(k) {
 p.Add(ek)
 }
 } else {
 p.Add(er)
 }
 p.Add(es)
 u.Add(&p)
 }
 powSet = powSet.Union(&u)
 }
 return powSet
}
func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
 o := other.(*threadUnsafeSet)
 cartProduct := NewThreadUnsafeSet()
 for i := range *set {
 for j := range *o {
 elem := OrderedPair{First: i, Second: j}
 cartProduct.Add(elem)
 }
 }
 return cartProduct
}
func (set *threadUnsafeSet) ToSlice() []interface{} {
 keys := make([]interface{}, 0, set.Cardinality())
 for elem := range *set {
 keys = append(keys, elem)
 }
 return keys
}
// MarshalJSON creates a JSON array from the set, it marshals all elements
func (set *threadUnsafeSet) MarshalJSON() ([]byte, error) {
 items := make([]string, 0, set.Cardinality())
 for elem := range *set {
 b, err := json.Marshal(elem)
 if err != nil {
 return nil, err
 }
 items = append(items, string(b))
 }
 return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
}
// UnmarshalJSON recreates a set from a JSON array, it only decodes
// primitive types. Numbers are decoded as json.Number.
func (set *threadUnsafeSet) UnmarshalJSON(b []byte) error {
 var i []interface{}
 d := json.NewDecoder(bytes.NewReader(b))
 d.UseNumber()
 err := d.Decode(&i)
 if err != nil {
 return err
 }
 for _, v := range i {
 switch t := v.(type) {
 case []interface{}, map[string]interface{}:
 continue
 default:
 set.Add(t)
 }
 }
 return nil
}
// threadsafe.go
package mapset
import "sync"
type threadSafeSet struct {
 s threadUnsafeSet
 sync.RWMutex
}
func newThreadSafeSet() threadSafeSet {
 return threadSafeSet{s: newThreadUnsafeSet()}
}
func (set *threadSafeSet) Add(i interface{}) bool {
 set.Lock()
 ret := set.s.Add(i)
 set.Unlock()
 return ret
}
func (set *threadSafeSet) Contains(i ...interface{}) bool {
 set.RLock()
 ret := set.s.Contains(i...)
 set.RUnlock()
 return ret
}
func (set *threadSafeSet) IsSubset(other Set) bool {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 ret := set.s.IsSubset(&o.s)
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) IsProperSubset(other Set) bool {
 o := other.(*threadSafeSet)
 set.RLock()
 defer set.RUnlock()
 o.RLock()
 defer o.RUnlock()
 return set.s.IsProperSubset(&o.s)
}
func (set *threadSafeSet) IsSuperset(other Set) bool {
 return other.IsSubset(set)
}
func (set *threadSafeSet) IsProperSuperset(other Set) bool {
 return other.IsProperSubset(set)
}
func (set *threadSafeSet) Union(other Set) Set {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 unsafeUnion := set.s.Union(&o.s).(*threadUnsafeSet)
 ret := &threadSafeSet{s: *unsafeUnion}
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) Intersect(other Set) Set {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 unsafeIntersection := set.s.Intersect(&o.s).(*threadUnsafeSet)
 ret := &threadSafeSet{s: *unsafeIntersection}
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) Difference(other Set) Set {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 unsafeDifference := set.s.Difference(&o.s).(*threadUnsafeSet)
 ret := &threadSafeSet{s: *unsafeDifference}
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) SymmetricDifference(other Set) Set {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 unsafeDifference := set.s.SymmetricDifference(&o.s).(*threadUnsafeSet)
 ret := &threadSafeSet{s: *unsafeDifference}
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) Clear() {
 set.Lock()
 set.s = newThreadUnsafeSet()
 set.Unlock()
}
func (set *threadSafeSet) Remove(i interface{}) {
 set.Lock()
 delete(set.s, i)
 set.Unlock()
}
func (set *threadSafeSet) Cardinality() int {
 set.RLock()
 defer set.RUnlock()
 return len(set.s)
}
func (set *threadSafeSet) Each(cb func(interface{}) bool) {
 set.RLock()
 for elem := range set.s {
 if cb(elem) {
 break
 }
 }
 set.RUnlock()
}
func (set *threadSafeSet) Iter() <-chan interface{} {
 ch := make(chan interface{})
 go func() {
 set.RLock()
 for elem := range set.s {
 ch <- elem
 }
 close(ch)
 set.RUnlock()
 }()
 return ch
}
func (set *threadSafeSet) Iterator() *Iterator {
 iterator, ch, stopCh := newIterator()
 go func() {
 set.RLock()
 L:
 for elem := range set.s {
 select {
 case <-stopCh:
 break L
 case ch <- elem:
 }
 }
 close(ch)
 set.RUnlock()
 }()
 return iterator
}
func (set *threadSafeSet) Equal(other Set) bool {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 ret := set.s.Equal(&o.s)
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) Clone() Set {
 set.RLock()
 unsafeClone := set.s.Clone().(*threadUnsafeSet)
 ret := &threadSafeSet{s: *unsafeClone}
 set.RUnlock()
 return ret
}
func (set *threadSafeSet) String() string {
 set.RLock()
 ret := set.s.String()
 set.RUnlock()
 return ret
}
func (set *threadSafeSet) PowerSet() Set {
 set.RLock()
 unsafePowerSet := set.s.PowerSet().(*threadUnsafeSet)
 set.RUnlock()
 ret := &threadSafeSet{s: newThreadUnsafeSet()}
 for subset := range unsafePowerSet.Iter() {
 unsafeSubset := subset.(*threadUnsafeSet)
 ret.Add(&threadSafeSet{s: *unsafeSubset})
 }
 return ret
}
func (set *threadSafeSet) Pop() interface{} {
 set.Lock()
 defer set.Unlock()
 return set.s.Pop()
}
func (set *threadSafeSet) CartesianProduct(other Set) Set {
 o := other.(*threadSafeSet)
 set.RLock()
 o.RLock()
 unsafeCartProduct := set.s.CartesianProduct(&o.s).(*threadUnsafeSet)
 ret := &threadSafeSet{s: *unsafeCartProduct}
 set.RUnlock()
 o.RUnlock()
 return ret
}
func (set *threadSafeSet) ToSlice() []interface{} {
 keys := make([]interface{}, 0, set.Cardinality())
 set.RLock()
 for elem := range set.s {
 keys = append(keys, elem)
 }
 set.RUnlock()
 return keys
}
func (set *threadSafeSet) MarshalJSON() ([]byte, error) {
 set.RLock()
 b, err := set.s.MarshalJSON()
 set.RUnlock()
 return b, err
}
func (set *threadSafeSet) UnmarshalJSON(p []byte) error {
 set.RLock()
 err := set.s.UnmarshalJSON(p)
 set.RUnlock()
 return err
}

图片描述

源码来自:https://github.com/deckarep/g...


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:李说的对

查看原文:golang练手小项目系列(6)-使用map实现set

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
938 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏