Skip to content

Commit

Permalink
Reintegrating optimizations originally proposed by @fy0 in issue:60 f…
Browse files Browse the repository at this point in the history
…or set instantiation to utilize capacity hint where possible (#113)
  • Loading branch information
deckarep authored Mar 14, 2023
1 parent d9a5ce2 commit 456a496
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 7 deletions.
29 changes: 23 additions & 6 deletions set.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ type Set[T comparable] interface {
// Remove removes a single element from the set.
Remove(i T)

// RemoveAll removes multiple elements from the set.
RemoveAll(i ...T)

// String provides a convenient string representation
// of the current state of the set.
String() string
Expand Down Expand Up @@ -182,27 +185,41 @@ type Set[T comparable] interface {
// NewSet creates and returns a new set with the given elements.
// Operations on the resulting set are thread-safe.
func NewSet[T comparable](vals ...T) Set[T] {
s := newThreadSafeSet[T]()
s := newThreadSafeSetWithSize[T](len(vals))
for _, item := range vals {
s.Add(item)
}
return s
}

// NewSetWithSize creates and returns a reference to an empty set with a specified
// capacity. Operations on the resulting set are thread-safe.
func NewSetWithSize[T comparable](cardinality int) Set[T] {
s := newThreadSafeSetWithSize[T](cardinality)
return s
}

// NewThreadUnsafeSet creates and returns a new set with the given elements.
// Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] {
s := newThreadUnsafeSet[T]()
s := newThreadUnsafeSetWithSize[T](len(vals))
for _, item := range vals {
s.Add(item)
}
return s
}

// Creates and returns a new set with the given keys of the map.
// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with
// a specified capacity. Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] {
s := newThreadUnsafeSetWithSize[T](cardinality)
return s
}

// NewSetFromMapKeys creates and returns a new set with the given keys of the map.
// Operations on the resulting set are thread-safe.
func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
s := NewSet[T]()
s := NewSetWithSize[T](len(val))

for k := range val {
s.Add(k)
Expand All @@ -211,10 +228,10 @@ func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
return s
}

// Creates and returns a new set with the given keys of the map.
// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map.
// Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
s := NewThreadUnsafeSet[T]()
s := NewThreadUnsafeSetWithSize[T](len(val))

for k := range val {
s.Add(k)
Expand Down
40 changes: 40 additions & 0 deletions set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,26 @@ func Test_RemoveSet(t *testing.T) {
}
}

func Test_RemoveAllSet(t *testing.T) {
a := makeSetInt([]int{6, 3, 1, 8, 9})

a.RemoveAll(3, 1)

if a.Cardinality() != 3 {
t.Error("RemoveAll should only have 2 items in the set")
}

if !a.Contains(6, 8, 9) {
t.Error("RemoveAll should have only items (6,8,9) in the set")
}

a.RemoveAll(6, 8, 9)

if a.Cardinality() != 0 {
t.Error("RemoveSet should be an empty set after removing 6 and 1")
}
}

func Test_RemoveUnsafeSet(t *testing.T) {
a := makeUnsafeSetInt([]int{6, 3, 1})

Expand All @@ -206,6 +226,26 @@ func Test_RemoveUnsafeSet(t *testing.T) {
}
}

func Test_RemoveAllUnsafeSet(t *testing.T) {
a := makeUnsafeSetInt([]int{6, 3, 1, 8, 9})

a.RemoveAll(3, 1)

if a.Cardinality() != 3 {
t.Error("RemoveAll should only have 2 items in the set")
}

if !a.Contains(6, 8, 9) {
t.Error("RemoveAll should have only items (6,8,9) in the set")
}

a.RemoveAll(6, 8, 9)

if a.Cardinality() != 0 {
t.Error("RemoveSet should be an empty set after removing 6 and 1")
}
}

func Test_ContainsSet(t *testing.T) {
a := NewSet[int]()

Expand Down
12 changes: 12 additions & 0 deletions threadsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ func newThreadSafeSet[T comparable]() *threadSafeSet[T] {
}
}

func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] {
return &threadSafeSet[T]{
uss: newThreadUnsafeSetWithSize[T](cardinality),
}
}

func (t *threadSafeSet[T]) Add(v T) bool {
t.Lock()
ret := t.uss.Add(v)
Expand Down Expand Up @@ -155,6 +161,12 @@ func (t *threadSafeSet[T]) Remove(v T) {
t.Unlock()
}

func (t *threadSafeSet[T]) RemoveAll(i ...T) {
t.Lock()
t.uss.RemoveAll(i...)
t.Unlock()
}

func (t *threadSafeSet[T]) Cardinality() int {
t.RLock()
defer t.RUnlock()
Expand Down
12 changes: 11 additions & 1 deletion threadunsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] {
return make(threadUnsafeSet[T])
}

func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] {
return make(threadUnsafeSet[T], cardinality)
}

func (s threadUnsafeSet[T]) Add(v T) bool {
prevLen := len(s)
s[v] = struct{}{}
Expand Down Expand Up @@ -74,7 +78,7 @@ func (s threadUnsafeSet[T]) Clear() {
}

func (s threadUnsafeSet[T]) Clone() Set[T] {
clonedSet := make(threadUnsafeSet[T], s.Cardinality())
clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality())
for elem := range s {
clonedSet.add(elem)
}
Expand Down Expand Up @@ -220,6 +224,12 @@ func (s threadUnsafeSet[T]) Remove(v T) {
delete(s, v)
}

func (s threadUnsafeSet[T]) RemoveAll(i ...T) {
for _, elem := range i {
delete(s, elem)
}
}

func (s threadUnsafeSet[T]) String() string {
items := make([]string, 0, len(s))

Expand Down

0 comments on commit 456a496

Please sign in to comment.