Skip to content

Commit fd9a09e

Browse files
committed
feat: Update set to Unitype set && Add test for basic operations
1 parent 1c9eec7 commit fd9a09e

File tree

2 files changed

+152
-30
lines changed

2 files changed

+152
-30
lines changed

hashset.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package hashset
22

3-
import "sync"
3+
import (
4+
"reflect"
5+
"sync"
6+
)
47

58
// Set represents a thread-safe collection of unique elements.
69
type Set struct {
@@ -23,6 +26,11 @@ func New(initial ...interface{}) *Set {
2326
func (s *Set) Add(element interface{}) {
2427
s.mu.Lock()
2528
defer s.mu.Unlock()
29+
if IsComparable(element) {
30+
s.hash[element] = true
31+
return
32+
}
33+
element = MakeComparable(element)
2634
s.hash[element] = true
2735
}
2836

@@ -124,3 +132,31 @@ func (s *Set) ToSlice() []interface{} {
124132
s.mu.RUnlock()
125133
return uniTypeSlice
126134
}
135+
136+
func MakeComparable(element interface{}) interface{} {
137+
/*
138+
Not comparable types: slice, map, function
139+
*/
140+
// defer func() {
141+
// if r := recover(); r != nil {
142+
// }
143+
// }()
144+
elementType := reflect.TypeOf(element)
145+
switch elementType.Kind() {
146+
case reflect.Slice, reflect.Map, reflect.Func:
147+
return reflect.ValueOf(element).Pointer()
148+
default:
149+
return element
150+
}
151+
}
152+
153+
// Powerful assertion comparable type by generic on compile time
154+
func IsComparable[T comparable](element T) bool {
155+
defer func() bool {
156+
if r := recover(); r != nil {
157+
return false
158+
}
159+
return true
160+
}()
161+
return element == element
162+
}

hashset_test.go

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,57 @@ import (
2424
3. difference
2525
2626
5. 거의 모든타입 지원이 되어야한다.
27-
1. 함수타입은 별도의 uuid와 포인터로 보관되어야한다.
28-
2. 구조체타입도 uuid로 할까 마샬링으로 할까 했는데 uuid가 좋아보이넹
29-
3027
6. 같은 set에 대해 동시작업이 원자성을 보장받아야한다.
3128
*/
3229

33-
// func testGongurrency(numOfGoroutine, numOfEashWork int, work func(prefix string, num int)) {
34-
// var wg sync.WaitGroup
35-
// for nthGoroutine := 0; nthGoroutine < numOfGoroutine; nthGoroutine++ {
36-
// wg.Add(1)
37-
// go func(n int) {
38-
// defer wg.Done()
39-
// for nthWork := 0; nthWork < numOfEashWork; nthWork++ {
40-
// work("testing-", nthWork)
41-
// }
42-
43-
// }(nthGoroutine)
44-
// }
45-
// wg.Wait()
46-
// }
30+
// Basic operations 0. Initialize
31+
func TestInitializeFromArguments(t *testing.T) {
32+
s := New(1, 2, "3")
33+
require.NotEmpty(t, s)
34+
require.Equal(t, 3, s.Len())
35+
36+
s.Add("3") // It lengths is 3, because Set already has "3"
37+
require.NotEqual(t, 4, s.Len())
38+
}
39+
40+
// Basic operations: 1. Add element
41+
func TestAddElement(t *testing.T) {
42+
s := New()
43+
require.NotEmpty(t, s)
44+
require.Equal(t, 0, s.Len())
45+
s.Add("Add")
46+
s.Add(" multiple")
47+
s.Add("values of")
48+
s.Add(3)
49+
s.Add("like this:")
50+
s.Add([]int{1, 2, 3})
51+
require.Equal(t, 6, s.Len())
52+
}
53+
54+
// Basic operations: 2. Remove element
55+
func TestRemoveElement(t *testing.T) {
56+
s := New("1", "2", 3)
57+
require.NotEmpty(t, s)
58+
require.Equal(t, 3, s.Len())
59+
s.Remove("1")
60+
s.Remove("multiple")
61+
s.Remove("3")
62+
require.Equal(t, 2, s.Len())
63+
}
64+
65+
// Basic operations: 3.
66+
// func TestMembershipCheck(t *testing.T) {}
67+
68+
// func TestDuplicate(t *testing.T) {}
69+
// func TestConvertToSet(t *testing.T) {}
70+
// func TestConvertToSlice(t *testing.T) {}
71+
// func TestUnion(t *testing.T) {}
72+
// func TestIntersection(t *testing.T) {}
73+
// func TestDifference(t *testing.T) {}
74+
// func TestFunctionElement(t *testing.T) {}
75+
// func TestStructElement(t *testing.T) {}
4776
func TestConcurrentAddElement10Goroutine100000Loop(t *testing.T) {
4877
var wg sync.WaitGroup
49-
5078
s := New()
5179
numOfGoroutine := 10
5280
numOfLoop := 100000
@@ -67,7 +95,6 @@ func TestConcurrentAddElement10Goroutine100000Loop(t *testing.T) {
6795
}
6896
func TestConcurrentAddElement100000Goroutine10Loop(t *testing.T) {
6997
var wg sync.WaitGroup
70-
7198
s := New()
7299
numOfGoroutine := 100000
73100
numOfLoop := 10
@@ -116,13 +143,72 @@ func TestConcurrentExclusiveLock100000Loop(t *testing.T) {
116143
require.Equal(t, 0, s.Len())
117144
}
118145

119-
// func TestConcurrentRemoveElement(t *testing.T) {}
120-
// func TestMembershipCheck(t *testing.T) {}
121-
// func TestDuplicate(t *testing.T) {}
122-
// func TestConvertToSet(t *testing.T) {}
123-
// func TestConvertToSlice(t *testing.T) {}
124-
// func TestUnion(t *testing.T) {}
125-
// func TestIntersection(t *testing.T) {}
126-
// func TestDifference(t *testing.T) {}
127-
// func TestFunctionElement(t *testing.T) {}
128-
// func TestStructElement(t *testing.T) {}
146+
func TestConcurrentRemoveElement(t *testing.T) {
147+
var wg sync.WaitGroup
148+
149+
s := New()
150+
numOfGoroutine := 1000
151+
numOfLoop := 10
152+
totalExpectElement := numOfGoroutine * numOfLoop
153+
154+
for nthGoroutine := 0; nthGoroutine < numOfGoroutine; nthGoroutine++ {
155+
wg.Add(1)
156+
go func(n int) {
157+
defer wg.Done()
158+
for nthWork := 0; nthWork < numOfLoop; nthWork++ {
159+
s.Add(strconv.Itoa(n) + ".testing-" + strconv.Itoa(nthWork))
160+
}
161+
162+
}(nthGoroutine)
163+
}
164+
wg.Wait()
165+
require.Equal(t, totalExpectElement, s.Len())
166+
167+
for nthGoroutine := 0; nthGoroutine < numOfGoroutine; nthGoroutine++ {
168+
wg.Add(1)
169+
go func(n int) {
170+
defer wg.Done()
171+
for nthWork := 0; nthWork < numOfLoop; nthWork++ {
172+
s.Remove(strconv.Itoa(n) + ".testing-" + strconv.Itoa(nthWork))
173+
}
174+
175+
}(nthGoroutine)
176+
}
177+
wg.Wait()
178+
require.Equal(t, 0, s.Len())
179+
}
180+
181+
func TestMembershipCheck(t *testing.T) {
182+
var wg sync.WaitGroup
183+
184+
s := New()
185+
numOfGoroutine := 10000
186+
numOfLoop := 10
187+
totalExpectElement := numOfGoroutine * numOfLoop
188+
189+
for nthGoroutine := 0; nthGoroutine < numOfGoroutine; nthGoroutine++ {
190+
wg.Add(1)
191+
go func(n int) {
192+
defer wg.Done()
193+
for nthWork := 0; nthWork < numOfLoop; nthWork++ {
194+
s.Add(strconv.Itoa(n) + ".testing-" + strconv.Itoa(nthWork))
195+
}
196+
197+
}(nthGoroutine)
198+
}
199+
wg.Wait()
200+
201+
require.Equal(t, totalExpectElement, s.Len())
202+
for nthGoroutine := 0; nthGoroutine < numOfGoroutine; nthGoroutine++ {
203+
wg.Add(1)
204+
go func(n int) {
205+
defer wg.Done()
206+
for nthWork := 0; nthWork < numOfLoop; nthWork++ {
207+
s.Add(strconv.Itoa(n) + ".testing-phase-two-" + strconv.Itoa(nthWork))
208+
s.Remove(strconv.Itoa(n) + ".testing-" + strconv.Itoa(nthWork))
209+
}
210+
211+
}(nthGoroutine)
212+
}
213+
wg.Wait()
214+
}

0 commit comments

Comments
 (0)