|
1 | 1 | package collection |
2 | 2 |
|
3 | | -import ( |
4 | | - "github.com/zeromicro/go-zero/core/lang" |
5 | | - "github.com/zeromicro/go-zero/core/logx" |
6 | | -) |
| 3 | +import "github.com/zeromicro/go-zero/core/lang" |
7 | 4 |
|
8 | | -const ( |
9 | | - unmanaged = iota |
10 | | - untyped |
11 | | - intType |
12 | | - int64Type |
13 | | - uintType |
14 | | - uint64Type |
15 | | - stringType |
16 | | -) |
17 | | - |
18 | | -// TypedSet is a type-safe generic set collection. It's not thread-safe, |
19 | | -// use with synchronization for concurrent access. |
20 | | -// |
21 | | -// Advantages over the legacy Set: |
22 | | -// - Compile-time type safety (no runtime type validation needed) |
23 | | -// - Better performance (no type assertions or reflection overhead) |
24 | | -// - Cleaner API (single Add method instead of multiple type-specific methods) |
25 | | -// - No need for type-specific Keys methods (KeysInt, KeysStr, etc.) |
26 | | -// - Zero-allocation for empty checks and direct type access |
27 | | -type TypedSet[T comparable] struct { |
| 5 | +// Set is a type-safe generic set collection. |
| 6 | +// It's not thread-safe, use with synchronization for concurrent access. |
| 7 | +type Set[T comparable] struct { |
28 | 8 | data map[T]lang.PlaceholderType |
29 | 9 | } |
30 | 10 |
|
31 | | -// NewTypedSet returns a new type-safe set. |
32 | | -func NewTypedSet[T comparable]() *TypedSet[T] { |
33 | | - return &TypedSet[T]{ |
| 11 | +// NewSet returns a new type-safe set. |
| 12 | +func NewSet[T comparable]() *Set[T] { |
| 13 | + return &Set[T]{ |
34 | 14 | data: make(map[T]lang.PlaceholderType), |
35 | 15 | } |
36 | 16 | } |
37 | 17 |
|
38 | | -// NewIntSet returns a new int-typed set. |
39 | | -func NewIntSet() *TypedSet[int] { |
40 | | - return NewTypedSet[int]() |
41 | | -} |
42 | | - |
43 | | -// NewInt64Set returns a new int64-typed set. |
44 | | -func NewInt64Set() *TypedSet[int64] { |
45 | | - return NewTypedSet[int64]() |
46 | | -} |
47 | | - |
48 | | -// NewUintSet returns a new uint-typed set. |
49 | | -func NewUintSet() *TypedSet[uint] { |
50 | | - return NewTypedSet[uint]() |
51 | | -} |
52 | | - |
53 | | -// NewUint64Set returns a new uint64-typed set. |
54 | | -func NewUint64Set() *TypedSet[uint64] { |
55 | | - return NewTypedSet[uint64]() |
56 | | -} |
57 | | - |
58 | | -// NewStringSet returns a new string-typed set. |
59 | | -func NewStringSet() *TypedSet[string] { |
60 | | - return NewTypedSet[string]() |
61 | | -} |
62 | | - |
63 | 18 | // Add adds items to the set. Duplicates are automatically ignored. |
64 | | -func (s *TypedSet[T]) Add(items ...T) { |
| 19 | +func (s *Set[T]) Add(items ...T) { |
65 | 20 | for _, item := range items { |
66 | 21 | s.data[item] = lang.Placeholder |
67 | 22 | } |
68 | 23 | } |
69 | 24 |
|
| 25 | +// Clear removes all items from the set. |
| 26 | +func (s *Set[T]) Clear() { |
| 27 | + clear(s.data) |
| 28 | +} |
| 29 | + |
70 | 30 | // Contains checks if an item exists in the set. |
71 | | -func (s *TypedSet[T]) Contains(item T) bool { |
| 31 | +func (s *Set[T]) Contains(item T) bool { |
72 | 32 | _, ok := s.data[item] |
73 | 33 | return ok |
74 | 34 | } |
75 | 35 |
|
76 | | -// Remove removes an item from the set. |
77 | | -func (s *TypedSet[T]) Remove(item T) { |
78 | | - delete(s.data, item) |
79 | | -} |
80 | | - |
81 | | -// Keys returns all elements in the set as a slice. |
82 | | -func (s *TypedSet[T]) Keys() []T { |
83 | | - keys := make([]T, 0, len(s.data)) |
84 | | - for key := range s.data { |
85 | | - keys = append(keys, key) |
86 | | - } |
87 | | - return keys |
88 | | -} |
89 | | - |
90 | 36 | // Count returns the number of items in the set. |
91 | | -func (s *TypedSet[T]) Count() int { |
| 37 | +func (s *Set[T]) Count() int { |
92 | 38 | return len(s.data) |
93 | 39 | } |
94 | 40 |
|
95 | | -// Clear removes all items from the set. |
96 | | -func (s *TypedSet[T]) Clear() { |
97 | | - s.data = make(map[T]lang.PlaceholderType) |
98 | | -} |
99 | | - |
100 | | -// Set is not thread-safe, for concurrent use, make sure to use it with synchronization. |
101 | | -// Deprecated: Use TypedSet[T] instead for better type safety and performance. |
102 | | -// TypedSet provides compile-time type checking and eliminates the need for type-specific methods. |
103 | | -type Set struct { |
104 | | - data map[any]lang.PlaceholderType |
105 | | - tp int |
106 | | -} |
107 | | - |
108 | | -// NewSet returns a managed Set, can only put the values with the same type. |
109 | | -// Deprecated: Use NewTypedSet[T]() instead for better type safety and performance. |
110 | | -// Example: NewIntSet() instead of NewSet() with AddInt() |
111 | | -func NewSet() *Set { |
112 | | - return &Set{ |
113 | | - data: make(map[any]lang.PlaceholderType), |
114 | | - tp: untyped, |
115 | | - } |
116 | | -} |
117 | | - |
118 | | -// NewUnmanagedSet returns an unmanaged Set, which can put values with different types. |
119 | | -// Deprecated: Use TypedSet[any] or multiple TypedSet instances for different types instead. |
120 | | -// If you really need mixed types, consider using map[any]struct{} directly. |
121 | | -func NewUnmanagedSet() *Set { |
122 | | - return &Set{ |
123 | | - data: make(map[any]lang.PlaceholderType), |
124 | | - tp: unmanaged, |
125 | | - } |
126 | | -} |
127 | | - |
128 | | -// Add adds i into s. |
129 | | -// Deprecated: Use TypedSet[T].Add() instead for better type safety and performance. |
130 | | -func (s *Set) Add(i ...any) { |
131 | | - for _, each := range i { |
132 | | - s.add(each) |
133 | | - } |
134 | | -} |
135 | | - |
136 | | -// AddInt adds int values ii into s. |
137 | | -// Deprecated: Use NewIntSet().Add() instead for better type safety and performance. |
138 | | -// Example: intSet := NewIntSet(); intSet.Add(1, 2, 3) |
139 | | -func (s *Set) AddInt(ii ...int) { |
140 | | - for _, each := range ii { |
141 | | - s.add(each) |
142 | | - } |
143 | | -} |
144 | | - |
145 | | -// AddInt64 adds int64 values ii into s. |
146 | | -// Deprecated: Use NewInt64Set().Add() instead for better type safety and performance. |
147 | | -// Example: int64Set := NewInt64Set(); int64Set.Add(1, 2, 3) |
148 | | -func (s *Set) AddInt64(ii ...int64) { |
149 | | - for _, each := range ii { |
150 | | - s.add(each) |
151 | | - } |
152 | | -} |
153 | | - |
154 | | -// AddUint adds uint values ii into s. |
155 | | -// Deprecated: Use NewUintSet().Add() instead for better type safety and performance. |
156 | | -// Example: uintSet := NewUintSet(); uintSet.Add(1, 2, 3) |
157 | | -func (s *Set) AddUint(ii ...uint) { |
158 | | - for _, each := range ii { |
159 | | - s.add(each) |
160 | | - } |
161 | | -} |
162 | | - |
163 | | -// AddUint64 adds uint64 values ii into s. |
164 | | -// Deprecated: Use NewUint64Set().Add() instead for better type safety and performance. |
165 | | -// Example: uint64Set := NewUint64Set(); uint64Set.Add(1, 2, 3) |
166 | | -func (s *Set) AddUint64(ii ...uint64) { |
167 | | - for _, each := range ii { |
168 | | - s.add(each) |
169 | | - } |
170 | | -} |
171 | | - |
172 | | -// AddStr adds string values ss into s. |
173 | | -// Deprecated: Use NewStringSet().Add() instead for better type safety and performance. |
174 | | -// Example: stringSet := NewStringSet(); stringSet.Add("a", "b", "c") |
175 | | -func (s *Set) AddStr(ss ...string) { |
176 | | - for _, each := range ss { |
177 | | - s.add(each) |
178 | | - } |
179 | | -} |
180 | | - |
181 | | -// Contains checks if i is in s. |
182 | | -// Deprecated: Use TypedSet[T].Contains() instead for better type safety and performance. |
183 | | -func (s *Set) Contains(i any) bool { |
184 | | - if len(s.data) == 0 { |
185 | | - return false |
186 | | - } |
187 | | - |
188 | | - s.validate(i) |
189 | | - _, ok := s.data[i] |
190 | | - return ok |
191 | | -} |
192 | | - |
193 | | -// Keys returns the keys in s. |
194 | | -// Deprecated: Use TypedSet[T].Keys() instead for better type safety and performance. |
195 | | -func (s *Set) Keys() []any { |
196 | | - var keys []any |
197 | | - |
| 41 | +// Keys returns all elements in the set as a slice. |
| 42 | +func (s *Set[T]) Keys() []T { |
| 43 | + keys := make([]T, 0, len(s.data)) |
198 | 44 | for key := range s.data { |
199 | 45 | keys = append(keys, key) |
200 | 46 | } |
201 | | - |
202 | 47 | return keys |
203 | 48 | } |
204 | 49 |
|
205 | | -// KeysInt returns the int keys in s. |
206 | | -// Deprecated: Use NewIntSet().Keys() instead for better type safety and performance. |
207 | | -// The TypedSet version returns []int directly without type casting. |
208 | | -func (s *Set) KeysInt() []int { |
209 | | - var keys []int |
210 | | - |
211 | | - for key := range s.data { |
212 | | - if intKey, ok := key.(int); ok { |
213 | | - keys = append(keys, intKey) |
214 | | - } |
215 | | - } |
216 | | - |
217 | | - return keys |
218 | | -} |
219 | | - |
220 | | -// KeysInt64 returns int64 keys in s. |
221 | | -// Deprecated: Use NewInt64Set().Keys() instead for better type safety and performance. |
222 | | -// The TypedSet version returns []int64 directly without type casting. |
223 | | -func (s *Set) KeysInt64() []int64 { |
224 | | - var keys []int64 |
225 | | - |
226 | | - for key := range s.data { |
227 | | - if intKey, ok := key.(int64); ok { |
228 | | - keys = append(keys, intKey) |
229 | | - } |
230 | | - } |
231 | | - |
232 | | - return keys |
233 | | -} |
234 | | - |
235 | | -// KeysUint returns uint keys in s. |
236 | | -// Deprecated: Use NewUintSet().Keys() instead for better type safety and performance. |
237 | | -// The TypedSet version returns []uint directly without type casting. |
238 | | -func (s *Set) KeysUint() []uint { |
239 | | - var keys []uint |
240 | | - |
241 | | - for key := range s.data { |
242 | | - if intKey, ok := key.(uint); ok { |
243 | | - keys = append(keys, intKey) |
244 | | - } |
245 | | - } |
246 | | - |
247 | | - return keys |
248 | | -} |
249 | | - |
250 | | -// KeysUint64 returns uint64 keys in s. |
251 | | -// |
252 | | -// Deprecated: Use NewUint64Set().Keys() instead for better type safety and performance. |
253 | | -// The TypedSet version returns []uint64 directly without type casting. |
254 | | -func (s *Set) KeysUint64() []uint64 { |
255 | | - var keys []uint64 |
256 | | - |
257 | | - for key := range s.data { |
258 | | - if intKey, ok := key.(uint64); ok { |
259 | | - keys = append(keys, intKey) |
260 | | - } |
261 | | - } |
262 | | - |
263 | | - return keys |
264 | | -} |
265 | | - |
266 | | -// KeysStr returns string keys in s. |
267 | | -// Deprecated: Use NewStringSet().Keys() instead for better type safety and performance. |
268 | | -// The TypedSet version returns []string directly without type casting. |
269 | | -func (s *Set) KeysStr() []string { |
270 | | - var keys []string |
271 | | - |
272 | | - for key := range s.data { |
273 | | - if strKey, ok := key.(string); ok { |
274 | | - keys = append(keys, strKey) |
275 | | - } |
276 | | - } |
277 | | - |
278 | | - return keys |
279 | | -} |
280 | | - |
281 | | -// Remove removes i from s. |
282 | | -// Deprecated: Use TypedSet[T].Remove() instead for better type safety and performance. |
283 | | -func (s *Set) Remove(i any) { |
284 | | - s.validate(i) |
285 | | - delete(s.data, i) |
286 | | -} |
287 | | - |
288 | | -// Count returns the number of items in s. |
289 | | -// Deprecated: Use TypedSet[T].Count() instead for better type safety and performance. |
290 | | -func (s *Set) Count() int { |
291 | | - return len(s.data) |
292 | | -} |
293 | | - |
294 | | -func (s *Set) add(i any) { |
295 | | - switch s.tp { |
296 | | - case unmanaged: |
297 | | - // do nothing |
298 | | - case untyped: |
299 | | - s.setType(i) |
300 | | - default: |
301 | | - s.validate(i) |
302 | | - } |
303 | | - s.data[i] = lang.Placeholder |
304 | | -} |
305 | | - |
306 | | -func (s *Set) setType(i any) { |
307 | | - // s.tp can only be untyped here |
308 | | - switch i.(type) { |
309 | | - case int: |
310 | | - s.tp = intType |
311 | | - case int64: |
312 | | - s.tp = int64Type |
313 | | - case uint: |
314 | | - s.tp = uintType |
315 | | - case uint64: |
316 | | - s.tp = uint64Type |
317 | | - case string: |
318 | | - s.tp = stringType |
319 | | - } |
320 | | -} |
321 | | - |
322 | | -func (s *Set) validate(i any) { |
323 | | - if s.tp == unmanaged { |
324 | | - return |
325 | | - } |
326 | | - |
327 | | - switch i.(type) { |
328 | | - case int: |
329 | | - if s.tp != intType { |
330 | | - logx.Errorf("element is int, but set contains elements with type %d", s.tp) |
331 | | - } |
332 | | - case int64: |
333 | | - if s.tp != int64Type { |
334 | | - logx.Errorf("element is int64, but set contains elements with type %d", s.tp) |
335 | | - } |
336 | | - case uint: |
337 | | - if s.tp != uintType { |
338 | | - logx.Errorf("element is uint, but set contains elements with type %d", s.tp) |
339 | | - } |
340 | | - case uint64: |
341 | | - if s.tp != uint64Type { |
342 | | - logx.Errorf("element is uint64, but set contains elements with type %d", s.tp) |
343 | | - } |
344 | | - case string: |
345 | | - if s.tp != stringType { |
346 | | - logx.Errorf("element is string, but set contains elements with type %d", s.tp) |
347 | | - } |
348 | | - } |
| 50 | +// Remove removes an item from the set. |
| 51 | +func (s *Set[T]) Remove(item T) { |
| 52 | + delete(s.data, item) |
349 | 53 | } |
0 commit comments