adding set

This commit is contained in:
William Dillon 2025-05-06 22:58:18 -04:00
parent 18916439ef
commit 3e8a006611
5 changed files with 245 additions and 0 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module set
go 1.24.3

24
set.go Normal file
View File

@ -0,0 +1,24 @@
package set
import (
"set/simpleset"
"set/threadsafeset"
)
type Set[T comparable] interface {
Insert(T) (inserted bool)
Contains(T) bool
Remove(T) (removed bool)
Len() int
ToSlice() []T
Clear()
}
func New[T comparable](threadsafe bool) Set[T] {
switch threadsafe {
case true:
return threadsafeset.New[T]()
default:
return simpleset.New[T]()
}
}

91
set_test.go Normal file
View File

@ -0,0 +1,91 @@
package set
import (
"testing"
)
func TestSimpleSet(t *testing.T) {
words := []string{"a", "b", "c"}
set := New[string](false)
for _, word := range words {
if !set.Insert(word) {
t.Fatalf("error: expected true while inserting '%s'; got false\n", word)
}
}
if want, got := len(words), set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
for _, word := range words {
if set.Insert(word) {
t.Fatalf("error: expected false during duplicate insert for '%s'; got true\n", word)
}
}
for _, word := range words {
if !set.Contains(word) {
t.Fatalf("error: didn't find word '%s' in set after second insert...\n", word)
}
}
for _, word := range words {
if !set.Remove(word) {
t.Fatalf("error removing word '%s'\n", word)
}
}
if want, got := 0, set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
for _, word := range words {
if !set.Insert(word) {
t.Fatalf("error: expected true while inserting '%s'; got false\n", word)
}
}
if want, got := len(words), set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
set.Clear()
if want, got := 0, set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
}
func TestThreadsafeSet(t *testing.T) {
words := []string{"a", "b", "c"}
set := New[string](true)
for _, word := range words {
if !set.Insert(word) {
t.Fatalf("error: expected true while inserting '%s'; got false\n", word)
}
}
if want, got := len(words), set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
for _, word := range words {
if set.Insert(word) {
t.Fatalf("error: expected false during duplicate insert for '%s'; got true\n", word)
}
}
for _, word := range words {
if !set.Contains(word) {
t.Fatalf("error: didn't find word '%s' in set after second insert...\n", word)
}
}
for _, word := range words {
if !set.Remove(word) {
t.Fatalf("error removing word '%s'\n", word)
}
}
if want, got := 0, set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
for _, word := range words {
if !set.Insert(word) {
t.Fatalf("error: expected true while inserting '%s'; got false\n", word)
}
}
if want, got := len(words), set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
set.Clear()
if want, got := 0, set.Len(); want != got {
t.Fatalf("error: wanted %d entries; got %d\n", want, got)
}
}

59
simpleset/simpleset.go Normal file
View File

@ -0,0 +1,59 @@
package simpleset
type SimpleSet[T comparable] struct {
set map[T]any
}
func New[T comparable]() *SimpleSet[T] {
return &SimpleSet[T]{
set: make(map[T]any),
}
}
func (s *SimpleSet[T]) lockedInitMapIfNil() {
if s.set == nil {
s.set = make(map[T]any)
}
}
func (s *SimpleSet[T]) Insert(t T) (inserted bool) {
s.lockedInitMapIfNil()
if _, found := s.set[t]; found {
return false
} else {
s.set[t] = struct{}{}
return true
}
}
func (s *SimpleSet[T]) Contains(t T) bool {
s.lockedInitMapIfNil()
_, found := s.set[t]
return found
}
func (s *SimpleSet[T]) Len() int {
s.lockedInitMapIfNil()
return len(s.set)
}
func (s *SimpleSet[T]) ToSlice() []T {
s.lockedInitMapIfNil()
results := make([]T, 0, s.Len())
for k := range s.set {
results = append(results, k)
}
return results
}
func (s *SimpleSet[T]) Clear() {
s.set = make(map[T]any)
}
func (s *SimpleSet[T]) Remove(t T) (removed bool) {
if !s.Contains(t) {
return false
}
delete(s.set, t)
return true
}

View File

@ -0,0 +1,68 @@
package threadsafeset
import (
"set/simpleset"
"sync"
)
type ThreadsafeSet[T comparable] struct {
mutex sync.RWMutex
set *simpleset.SimpleSet[T]
}
func New[T comparable]() *ThreadsafeSet[T] {
return &ThreadsafeSet[T]{
set: simpleset.New[T](),
}
}
func (s *ThreadsafeSet[T]) lockedInitMapIfNil() {
if s.set == nil {
s.set = simpleset.New[T]()
}
}
func (s *ThreadsafeSet[T]) Insert(t T) (inserted bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.lockedInitMapIfNil()
return s.set.Insert(t)
}
func (s *ThreadsafeSet[T]) Contains(t T) bool {
s.mutex.RLock()
defer s.mutex.RUnlock()
return s.set != nil && s.set.Contains(t)
}
func (s *ThreadsafeSet[T]) Len() int {
s.mutex.RLock()
defer s.mutex.RUnlock()
if s.set == nil {
return 0
}
return s.set.Len()
}
func (s *ThreadsafeSet[T]) ToSlice() []T {
s.mutex.RLock()
defer s.mutex.RUnlock()
if s.set == nil {
return nil
}
return s.set.ToSlice()
}
func (s *ThreadsafeSet[T]) Clear() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.lockedInitMapIfNil()
s.set.Clear()
}
func (s *ThreadsafeSet[T]) Remove(t T) (removed bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.lockedInitMapIfNil()
return s.set.Remove(t)
}