├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── bench_test.go ├── go.mod ├── go.sum ├── iterator.go ├── iterator_example_test.go ├── new_improved.jpeg ├── set.go ├── set123_test.go ├── set_test.go ├── sorted.go ├── sorted_test.go ├── threadsafe.go ├── threadsafe_test.go ├── threadunsafe.go └── threadunsafe_test.go /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: CI 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x, 1.24.x] 8 | os: [ubuntu-latest, macos-latest, windows-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - name: Install Go 12 | uses: actions/setup-go@v5 13 | with: 14 | go-version: ${{ matrix.go-version }} 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | - name: Test 18 | run: | 19 | go test -v -race ./... 20 | # go vet ./... 21 | # go test -bench=. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Open Source Initiative OSI - The MIT License (MIT):Licensing 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2) 3 | [![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2) 4 | 5 | # golang-set 6 | 7 | The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. 8 | 9 | ## Psst 10 | * Hi there, 👋! Do you use or have interest in the [Zig programming language](https://ziglang.org/) created by Andrew Kelley? If so, the golang-set project has a new sibling project: [ziglang-set](https://github.com/deckarep/ziglang-set)! Come check it out! 11 | 12 | ## Update 3/14/2025 13 | * Packaged version: `2.8.0` introduces support for true iterators for Go 1.23+. Please see [issue #141](https://github.com/deckarep/golang-set/issues/141) 14 | for further details on the implications of how iterations work between older Go versions vs newer Go versions. Additionally, this 15 | release has a minor unit-test spelling fix. 16 | 17 | ## Update 12/3/2024 18 | * Packaged version: `2.7.0` fixes a long-standing bug with *JSON Unmarshaling*. A large refactor in the interest of performance 19 | introduced this bug and there was no way around it but to revert the code back to how it was previously. The performance 20 | difference was likely negligible to begin with. JSON Marshaling and Unmarshaling is now properly supported again without 21 | needing to do workarounds. 22 | 23 | ## Update 3/5/2023 24 | * Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`. 25 | * supports `new generic` syntax 26 | * Go `1.18.0` or higher 27 | * Workflow tested on Go `1.20` 28 | 29 | ![With Generics](new_improved.jpeg) 30 | 31 | Coming from Python one of the things I miss is the superbly wonderful set collection. This is my attempt to mimic the primary features of the set collection from Python. 32 | You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library. To those I say simply ignore this repository and carry-on and to the rest that find this useful please contribute in helping me make it better by contributing with suggestions or PRs. 33 | 34 | ## Install 35 | 36 | Use `go get` to install this package. 37 | 38 | ```shell 39 | go get github.com/deckarep/golang-set/v2 40 | ``` 41 | 42 | ## Features 43 | 44 | * *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) 45 | * One common *interface* to both implementations 46 | * a **non threadsafe** implementation favoring *performance* 47 | * a **threadsafe** implementation favoring *concurrent* use 48 | * Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set). 49 | * Exhaustive unit-test and benchmark suite 50 | 51 | ## Trusted by 52 | 53 | This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package. 54 | 55 | * Notable projects/companies using this package 56 | * Ethereum 57 | * Docker 58 | * 1Password 59 | * Hashicorp 60 | 61 | ## Star History 62 | 63 | [![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date) 64 | 65 | 66 | ## Usage 67 | 68 | The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object. 69 | 70 | What is considered comparable in Go? 71 | * `Booleans`, `integers`, `strings`, `floats` or basically primitive types. 72 | * `Pointers` 73 | * `Arrays` 74 | * `Structs` if *all of their fields* are also comparable independently 75 | 76 | Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection. 77 | 78 | ```go 79 | // Syntax example, doesn't compile. 80 | mySet := mapset.NewSet[T]() // where T is some concrete comparable type. 81 | 82 | // Therefore this code creates an int set 83 | mySet := mapset.NewSet[int]() 84 | 85 | // Or perhaps you want a string set 86 | mySet := mapset.NewSet[string]() 87 | 88 | type myStruct struct { 89 | name string 90 | age uint8 91 | } 92 | 93 | // Alternatively a set of structs 94 | mySet := mapset.NewSet[myStruct]() 95 | 96 | // Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety. 97 | mySet := mapset.NewSet[any]() 98 | ``` 99 | 100 | ## Comprehensive Example 101 | 102 | ```go 103 | package main 104 | 105 | import ( 106 | "fmt" 107 | mapset "github.com/deckarep/golang-set/v2" 108 | ) 109 | 110 | func main() { 111 | // Create a string-based set of required classes. 112 | required := mapset.NewSet[string]() 113 | required.Add("cooking") 114 | required.Add("english") 115 | required.Add("math") 116 | required.Add("biology") 117 | 118 | // Create a string-based set of science classes. 119 | sciences := mapset.NewSet[string]() 120 | sciences.Add("biology") 121 | sciences.Add("chemistry") 122 | 123 | // Create a string-based set of electives. 124 | electives := mapset.NewSet[string]() 125 | electives.Add("welding") 126 | electives.Add("music") 127 | electives.Add("automotive") 128 | 129 | // Create a string-based set of bonus programming classes. 130 | bonus := mapset.NewSet[string]() 131 | bonus.Add("beginner go") 132 | bonus.Add("python for dummies") 133 | } 134 | ``` 135 | 136 | Create a set of all unique classes. 137 | Sets will *automatically* deduplicate the same data. 138 | 139 | ```go 140 | all := required 141 | .Union(sciences) 142 | .Union(electives) 143 | .Union(bonus) 144 | 145 | fmt.Println(all) 146 | ``` 147 | 148 | Output: 149 | ```sh 150 | Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies} 151 | ``` 152 | 153 | Is cooking considered a science class? 154 | ```go 155 | result := sciences.Contains("cooking") 156 | fmt.Println(result) 157 | ``` 158 | 159 | Output: 160 | ```false 161 | false 162 | ``` 163 | 164 | Show me all classes that are not science classes, since I don't enjoy science. 165 | ```go 166 | notScience := all.Difference(sciences) 167 | fmt.Println(notScience) 168 | ``` 169 | 170 | ```sh 171 | Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding } 172 | ``` 173 | 174 | Which science classes are also required classes? 175 | ```go 176 | reqScience := sciences.Intersect(required) 177 | ``` 178 | 179 | Output: 180 | ```sh 181 | Set{biology} 182 | ``` 183 | 184 | How many bonus classes do you offer? 185 | ```go 186 | fmt.Println(bonus.Cardinality()) 187 | ``` 188 | Output: 189 | ```sh 190 | 2 191 | ``` 192 | 193 | Thanks for visiting! 194 | 195 | -deckarep 196 | -------------------------------------------------------------------------------- /bench_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "math/rand" 30 | "testing" 31 | ) 32 | 33 | func nrand(n int) []int { 34 | i := make([]int, n) 35 | for ind := range i { 36 | i[ind] = rand.Int() 37 | } 38 | return i 39 | } 40 | 41 | func benchAdd(b *testing.B, n int, newSet func(...int) Set[int]) { 42 | nums := nrand(n) 43 | b.ResetTimer() 44 | for i := 0; i < b.N; i++ { 45 | s := newSet() 46 | for _, v := range nums { 47 | s.Add(v) 48 | } 49 | } 50 | } 51 | 52 | func BenchmarkAddSafe(b *testing.B) { 53 | benchAdd(b, 1000, NewSet[int]) 54 | } 55 | 56 | func BenchmarkAddUnsafe(b *testing.B) { 57 | benchAdd(b, 1000, NewThreadUnsafeSet[int]) 58 | } 59 | 60 | func benchRemove(b *testing.B, s Set[int]) { 61 | nums := nrand(b.N) 62 | for _, v := range nums { 63 | s.Add(v) 64 | } 65 | 66 | b.ResetTimer() 67 | for _, v := range nums { 68 | s.Remove(v) 69 | } 70 | } 71 | 72 | func BenchmarkRemoveSafe(b *testing.B) { 73 | benchRemove(b, NewSet[int]()) 74 | } 75 | 76 | func BenchmarkRemoveUnsafe(b *testing.B) { 77 | benchRemove(b, NewThreadUnsafeSet[int]()) 78 | } 79 | 80 | func benchCardinality(b *testing.B, s Set[int]) { 81 | for i := 0; i < b.N; i++ { 82 | s.Cardinality() 83 | } 84 | } 85 | 86 | func BenchmarkCardinalitySafe(b *testing.B) { 87 | benchCardinality(b, NewSet[int]()) 88 | } 89 | 90 | func BenchmarkCardinalityUnsafe(b *testing.B) { 91 | benchCardinality(b, NewThreadUnsafeSet[int]()) 92 | } 93 | 94 | func benchClear(b *testing.B, s Set[int]) { 95 | b.ResetTimer() 96 | for i := 0; i < b.N; i++ { 97 | s.Clear() 98 | } 99 | } 100 | 101 | func BenchmarkClearSafe(b *testing.B) { 102 | benchClear(b, NewSet[int]()) 103 | } 104 | 105 | func BenchmarkClearUnsafe(b *testing.B) { 106 | benchClear(b, NewThreadUnsafeSet[int]()) 107 | } 108 | 109 | func benchClone(b *testing.B, n int, s Set[int]) { 110 | nums := nrand(n) 111 | for _, v := range nums { 112 | s.Add(v) 113 | } 114 | 115 | b.ResetTimer() 116 | for i := 0; i < b.N; i++ { 117 | s.Clone() 118 | } 119 | } 120 | 121 | func BenchmarkClone1Safe(b *testing.B) { 122 | benchClone(b, 1, NewSet[int]()) 123 | } 124 | 125 | func BenchmarkClone1Unsafe(b *testing.B) { 126 | benchClone(b, 1, NewThreadUnsafeSet[int]()) 127 | } 128 | 129 | func BenchmarkClone10Safe(b *testing.B) { 130 | benchClone(b, 10, NewSet[int]()) 131 | } 132 | 133 | func BenchmarkClone10Unsafe(b *testing.B) { 134 | benchClone(b, 10, NewThreadUnsafeSet[int]()) 135 | } 136 | 137 | func BenchmarkClone100Safe(b *testing.B) { 138 | benchClone(b, 100, NewSet[int]()) 139 | } 140 | 141 | func BenchmarkClone100Unsafe(b *testing.B) { 142 | benchClone(b, 100, NewThreadUnsafeSet[int]()) 143 | } 144 | 145 | func benchContains(b *testing.B, n int, s Set[int]) { 146 | nums := nrand(n) 147 | for _, v := range nums { 148 | s.Add(v) 149 | } 150 | 151 | nums[n-1] = -1 // Definitely not in s 152 | 153 | b.ResetTimer() 154 | for i := 0; i < b.N; i++ { 155 | s.Contains(nums...) 156 | } 157 | } 158 | 159 | func BenchmarkContains1Safe(b *testing.B) { 160 | benchContains(b, 1, NewSet[int]()) 161 | } 162 | 163 | func BenchmarkContains1Unsafe(b *testing.B) { 164 | benchContains(b, 1, NewThreadUnsafeSet[int]()) 165 | } 166 | 167 | func BenchmarkContains10Safe(b *testing.B) { 168 | benchContains(b, 10, NewSet[int]()) 169 | } 170 | 171 | func BenchmarkContains10Unsafe(b *testing.B) { 172 | benchContains(b, 10, NewThreadUnsafeSet[int]()) 173 | } 174 | 175 | func BenchmarkContains100Safe(b *testing.B) { 176 | benchContains(b, 100, NewSet[int]()) 177 | } 178 | 179 | func BenchmarkContains100Unsafe(b *testing.B) { 180 | benchContains(b, 100, NewThreadUnsafeSet[int]()) 181 | } 182 | 183 | func benchContainsOne(b *testing.B, n int, s Set[int]) { 184 | nums := nrand(n) 185 | for _, v := range nums { 186 | s.Add(v) 187 | } 188 | 189 | b.ResetTimer() 190 | for i := 0; i < b.N; i++ { 191 | s.ContainsOne(-1) 192 | } 193 | } 194 | 195 | func BenchmarkContainsOne1Safe(b *testing.B) { 196 | benchContainsOne(b, 1, NewSet[int]()) 197 | } 198 | 199 | func BenchmarkContainsOne1Unsafe(b *testing.B) { 200 | benchContainsOne(b, 1, NewThreadUnsafeSet[int]()) 201 | } 202 | 203 | func BenchmarkContainsOne10Safe(b *testing.B) { 204 | benchContainsOne(b, 10, NewSet[int]()) 205 | } 206 | 207 | func BenchmarkContainsOne10Unsafe(b *testing.B) { 208 | benchContainsOne(b, 10, NewThreadUnsafeSet[int]()) 209 | } 210 | 211 | func BenchmarkContainsOne100Safe(b *testing.B) { 212 | benchContainsOne(b, 100, NewSet[int]()) 213 | } 214 | 215 | func BenchmarkContainsOne100Unsafe(b *testing.B) { 216 | benchContainsOne(b, 100, NewThreadUnsafeSet[int]()) 217 | } 218 | 219 | // In this scenario, Contains argument escapes to the heap, while ContainsOne does not. 220 | func benchContainsComparison(b *testing.B, n int, s Set[int]) { 221 | nums := nrand(n) 222 | for _, v := range nums { 223 | s.Add(v) 224 | } 225 | 226 | b.Run("Contains", func(b *testing.B) { 227 | b.ReportAllocs() 228 | for i := 0; i < b.N; i++ { 229 | for _, v := range nums { 230 | s.Contains(v) // 1 allocation, v is moved to the heap 231 | } 232 | } 233 | }) 234 | b.Run("Contains slice", func(b *testing.B) { 235 | b.ReportAllocs() 236 | for i := 0; i < b.N; i++ { 237 | for i := range nums { 238 | s.Contains(nums[i : i+1]...) // no allocations, using heap-allocated slice 239 | } 240 | } 241 | }) 242 | b.Run("ContainsOne", func(b *testing.B) { 243 | b.ReportAllocs() 244 | for i := 0; i < b.N; i++ { 245 | for _, v := range nums { 246 | s.ContainsOne(v) // no allocations, using stack-allocated v 247 | } 248 | } 249 | }) 250 | } 251 | 252 | func BenchmarkContainsComparison1Unsafe(b *testing.B) { 253 | benchContainsComparison(b, 1, NewThreadUnsafeSet[int]()) 254 | } 255 | 256 | func BenchmarkContainsComparison1Safe(b *testing.B) { 257 | benchContainsComparison(b, 1, NewSet[int]()) 258 | } 259 | 260 | func BenchmarkContainsComparison10Unsafe(b *testing.B) { 261 | benchContainsComparison(b, 10, NewThreadUnsafeSet[int]()) 262 | } 263 | 264 | func BenchmarkContainsComparison10Safe(b *testing.B) { 265 | benchContainsComparison(b, 10, NewSet[int]()) 266 | } 267 | 268 | func BenchmarkContainsComparison100Unsafe(b *testing.B) { 269 | benchContainsComparison(b, 100, NewThreadUnsafeSet[int]()) 270 | } 271 | 272 | func BenchmarkContainsComparison100Safe(b *testing.B) { 273 | benchContainsComparison(b, 100, NewSet[int]()) 274 | } 275 | 276 | func benchEqual(b *testing.B, n int, s, t Set[int]) { 277 | nums := nrand(n) 278 | for _, v := range nums { 279 | s.Add(v) 280 | t.Add(v) 281 | } 282 | 283 | b.ResetTimer() 284 | for i := 0; i < b.N; i++ { 285 | s.Equal(t) 286 | } 287 | } 288 | 289 | func BenchmarkEqual1Safe(b *testing.B) { 290 | benchEqual(b, 1, NewSet[int](), NewSet[int]()) 291 | } 292 | 293 | func BenchmarkEqual1Unsafe(b *testing.B) { 294 | benchEqual(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 295 | } 296 | 297 | func BenchmarkEqual10Safe(b *testing.B) { 298 | benchEqual(b, 10, NewSet[int](), NewSet[int]()) 299 | } 300 | 301 | func BenchmarkEqual10Unsafe(b *testing.B) { 302 | benchEqual(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 303 | } 304 | 305 | func BenchmarkEqual100Safe(b *testing.B) { 306 | benchEqual(b, 100, NewSet[int](), NewSet[int]()) 307 | } 308 | 309 | func BenchmarkEqual100Unsafe(b *testing.B) { 310 | benchEqual(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 311 | } 312 | 313 | func benchDifference(b *testing.B, n int, s, t Set[int]) { 314 | nums := nrand(n) 315 | for _, v := range nums { 316 | s.Add(v) 317 | } 318 | for _, v := range nums[:n/2] { 319 | t.Add(v) 320 | } 321 | 322 | b.ResetTimer() 323 | for i := 0; i < b.N; i++ { 324 | s.Difference(t) 325 | } 326 | } 327 | 328 | func benchIsSubset(b *testing.B, n int, s, t Set[int]) { 329 | nums := nrand(n) 330 | for _, v := range nums { 331 | s.Add(v) 332 | t.Add(v) 333 | } 334 | 335 | b.ResetTimer() 336 | for i := 0; i < b.N; i++ { 337 | s.IsSubset(t) 338 | } 339 | } 340 | 341 | func BenchmarkIsSubset1Safe(b *testing.B) { 342 | benchIsSubset(b, 1, NewSet[int](), NewSet[int]()) 343 | } 344 | 345 | func BenchmarkIsSubset1Unsafe(b *testing.B) { 346 | benchIsSubset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 347 | } 348 | 349 | func BenchmarkIsSubset10Safe(b *testing.B) { 350 | benchIsSubset(b, 10, NewSet[int](), NewSet[int]()) 351 | } 352 | 353 | func BenchmarkIsSubset10Unsafe(b *testing.B) { 354 | benchIsSubset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 355 | } 356 | 357 | func BenchmarkIsSubset100Safe(b *testing.B) { 358 | benchIsSubset(b, 100, NewSet[int](), NewSet[int]()) 359 | } 360 | 361 | func BenchmarkIsSubset100Unsafe(b *testing.B) { 362 | benchIsSubset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 363 | } 364 | 365 | func benchIsSuperset(b *testing.B, n int, s, t Set[int]) { 366 | nums := nrand(n) 367 | for _, v := range nums { 368 | s.Add(v) 369 | t.Add(v) 370 | } 371 | 372 | b.ResetTimer() 373 | for i := 0; i < b.N; i++ { 374 | s.IsSuperset(t) 375 | } 376 | } 377 | 378 | func BenchmarkIsSuperset1Safe(b *testing.B) { 379 | benchIsSuperset(b, 1, NewSet[int](), NewSet[int]()) 380 | } 381 | 382 | func BenchmarkIsSuperset1Unsafe(b *testing.B) { 383 | benchIsSuperset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 384 | } 385 | 386 | func BenchmarkIsSuperset10Safe(b *testing.B) { 387 | benchIsSuperset(b, 10, NewSet[int](), NewSet[int]()) 388 | } 389 | 390 | func BenchmarkIsSuperset10Unsafe(b *testing.B) { 391 | benchIsSuperset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 392 | } 393 | 394 | func BenchmarkIsSuperset100Safe(b *testing.B) { 395 | benchIsSuperset(b, 100, NewSet[int](), NewSet[int]()) 396 | } 397 | 398 | func BenchmarkIsSuperset100Unsafe(b *testing.B) { 399 | benchIsSuperset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 400 | } 401 | 402 | func benchIsProperSubset(b *testing.B, n int, s, t Set[int]) { 403 | nums := nrand(n) 404 | for _, v := range nums { 405 | s.Add(v) 406 | t.Add(v) 407 | } 408 | 409 | b.ResetTimer() 410 | for i := 0; i < b.N; i++ { 411 | s.IsProperSubset(t) 412 | } 413 | } 414 | 415 | func BenchmarkIsProperSubset1Safe(b *testing.B) { 416 | benchIsProperSubset(b, 1, NewSet[int](), NewSet[int]()) 417 | } 418 | 419 | func BenchmarkIsProperSubset1Unsafe(b *testing.B) { 420 | benchIsProperSubset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 421 | } 422 | 423 | func BenchmarkIsProperSubset10Safe(b *testing.B) { 424 | benchIsProperSubset(b, 10, NewSet[int](), NewSet[int]()) 425 | } 426 | 427 | func BenchmarkIsProperSubset10Unsafe(b *testing.B) { 428 | benchIsProperSubset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 429 | } 430 | 431 | func BenchmarkIsProperSubset100Safe(b *testing.B) { 432 | benchIsProperSubset(b, 100, NewSet[int](), NewSet[int]()) 433 | } 434 | 435 | func BenchmarkIsProperSubset100Unsafe(b *testing.B) { 436 | benchIsProperSubset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 437 | } 438 | 439 | func benchIsProperSuperset(b *testing.B, n int, s, t Set[int]) { 440 | nums := nrand(n) 441 | for _, v := range nums { 442 | s.Add(v) 443 | t.Add(v) 444 | } 445 | 446 | b.ResetTimer() 447 | for i := 0; i < b.N; i++ { 448 | s.IsProperSuperset(t) 449 | } 450 | } 451 | 452 | func BenchmarkIsProperSuperset1Safe(b *testing.B) { 453 | benchIsProperSuperset(b, 1, NewSet[int](), NewSet[int]()) 454 | } 455 | 456 | func BenchmarkIsProperSuperset1Unsafe(b *testing.B) { 457 | benchIsProperSuperset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 458 | } 459 | 460 | func BenchmarkIsProperSuperset10Safe(b *testing.B) { 461 | benchIsProperSuperset(b, 10, NewSet[int](), NewSet[int]()) 462 | } 463 | 464 | func BenchmarkIsProperSuperset10Unsafe(b *testing.B) { 465 | benchIsProperSuperset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 466 | } 467 | 468 | func BenchmarkIsProperSuperset100Safe(b *testing.B) { 469 | benchIsProperSuperset(b, 100, NewSet[int](), NewSet[int]()) 470 | } 471 | 472 | func BenchmarkIsProperSuperset100Unsafe(b *testing.B) { 473 | benchIsProperSuperset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 474 | } 475 | 476 | func BenchmarkDifference1Safe(b *testing.B) { 477 | benchDifference(b, 1, NewSet[int](), NewSet[int]()) 478 | } 479 | 480 | func BenchmarkDifference1Unsafe(b *testing.B) { 481 | benchDifference(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 482 | } 483 | 484 | func BenchmarkDifference10Safe(b *testing.B) { 485 | benchDifference(b, 10, NewSet[int](), NewSet[int]()) 486 | } 487 | 488 | func BenchmarkDifference10Unsafe(b *testing.B) { 489 | benchDifference(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 490 | } 491 | 492 | func BenchmarkDifference100Safe(b *testing.B) { 493 | benchDifference(b, 100, NewSet[int](), NewSet[int]()) 494 | } 495 | 496 | func BenchmarkDifference100Unsafe(b *testing.B) { 497 | benchDifference(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 498 | } 499 | 500 | func benchIntersect(b *testing.B, n int, s, t Set[int]) { 501 | nums := nrand(int(float64(n) * float64(1.5))) 502 | for _, v := range nums[:n] { 503 | s.Add(v) 504 | } 505 | for _, v := range nums[n/2:] { 506 | t.Add(v) 507 | } 508 | 509 | b.ResetTimer() 510 | for i := 0; i < b.N; i++ { 511 | s.Intersect(t) 512 | } 513 | } 514 | 515 | func BenchmarkIntersect1Safe(b *testing.B) { 516 | benchIntersect(b, 1, NewSet[int](), NewSet[int]()) 517 | } 518 | 519 | func BenchmarkIntersect1Unsafe(b *testing.B) { 520 | benchIntersect(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 521 | } 522 | 523 | func BenchmarkIntersect10Safe(b *testing.B) { 524 | benchIntersect(b, 10, NewSet[int](), NewSet[int]()) 525 | } 526 | 527 | func BenchmarkIntersect10Unsafe(b *testing.B) { 528 | benchIntersect(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 529 | } 530 | 531 | func BenchmarkIntersect100Safe(b *testing.B) { 532 | benchIntersect(b, 100, NewSet[int](), NewSet[int]()) 533 | } 534 | 535 | func BenchmarkIntersect100Unsafe(b *testing.B) { 536 | benchIntersect(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 537 | } 538 | 539 | func benchSymmetricDifference(b *testing.B, n int, s, t Set[int]) { 540 | nums := nrand(int(float64(n) * float64(1.5))) 541 | for _, v := range nums[:n] { 542 | s.Add(v) 543 | } 544 | for _, v := range nums[n/2:] { 545 | t.Add(v) 546 | } 547 | 548 | b.ResetTimer() 549 | for i := 0; i < b.N; i++ { 550 | s.SymmetricDifference(t) 551 | } 552 | } 553 | 554 | func BenchmarkSymmetricDifference1Safe(b *testing.B) { 555 | benchSymmetricDifference(b, 1, NewSet[int](), NewSet[int]()) 556 | } 557 | 558 | func BenchmarkSymmetricDifference1Unsafe(b *testing.B) { 559 | benchSymmetricDifference(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 560 | } 561 | 562 | func BenchmarkSymmetricDifference10Safe(b *testing.B) { 563 | benchSymmetricDifference(b, 10, NewSet[int](), NewSet[int]()) 564 | } 565 | 566 | func BenchmarkSymmetricDifference10Unsafe(b *testing.B) { 567 | benchSymmetricDifference(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 568 | } 569 | 570 | func BenchmarkSymmetricDifference100Safe(b *testing.B) { 571 | benchSymmetricDifference(b, 100, NewSet[int](), NewSet[int]()) 572 | } 573 | 574 | func BenchmarkSymmetricDifference100Unsafe(b *testing.B) { 575 | benchSymmetricDifference(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 576 | } 577 | 578 | func benchUnion(b *testing.B, n int, s, t Set[int]) { 579 | nums := nrand(n) 580 | for _, v := range nums[:n/2] { 581 | s.Add(v) 582 | } 583 | for _, v := range nums[n/2:] { 584 | t.Add(v) 585 | } 586 | 587 | b.ResetTimer() 588 | for i := 0; i < b.N; i++ { 589 | s.Union(t) 590 | } 591 | } 592 | 593 | func BenchmarkUnion1Safe(b *testing.B) { 594 | benchUnion(b, 1, NewSet[int](), NewSet[int]()) 595 | } 596 | 597 | func BenchmarkUnion1Unsafe(b *testing.B) { 598 | benchUnion(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 599 | } 600 | 601 | func BenchmarkUnion10Safe(b *testing.B) { 602 | benchUnion(b, 10, NewSet[int](), NewSet[int]()) 603 | } 604 | 605 | func BenchmarkUnion10Unsafe(b *testing.B) { 606 | benchUnion(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 607 | } 608 | 609 | func BenchmarkUnion100Safe(b *testing.B) { 610 | benchUnion(b, 100, NewSet[int](), NewSet[int]()) 611 | } 612 | 613 | func BenchmarkUnion100Unsafe(b *testing.B) { 614 | benchUnion(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]()) 615 | } 616 | 617 | func benchEach(b *testing.B, n int, s Set[int]) { 618 | nums := nrand(n) 619 | for _, v := range nums { 620 | s.Add(v) 621 | } 622 | 623 | b.ResetTimer() 624 | for i := 0; i < b.N; i++ { 625 | s.Each(func(elem int) bool { 626 | return false 627 | }) 628 | } 629 | } 630 | 631 | func BenchmarkEach1Safe(b *testing.B) { 632 | benchEach(b, 1, NewSet[int]()) 633 | } 634 | 635 | func BenchmarkEach1Unsafe(b *testing.B) { 636 | benchEach(b, 1, NewThreadUnsafeSet[int]()) 637 | } 638 | 639 | func BenchmarkEach10Safe(b *testing.B) { 640 | benchEach(b, 10, NewSet[int]()) 641 | } 642 | 643 | func BenchmarkEach10Unsafe(b *testing.B) { 644 | benchEach(b, 10, NewThreadUnsafeSet[int]()) 645 | } 646 | 647 | func BenchmarkEach100Safe(b *testing.B) { 648 | benchEach(b, 100, NewSet[int]()) 649 | } 650 | 651 | func BenchmarkEach100Unsafe(b *testing.B) { 652 | benchEach(b, 100, NewThreadUnsafeSet[int]()) 653 | } 654 | 655 | func benchIter(b *testing.B, n int, s Set[int]) { 656 | nums := nrand(n) 657 | for _, v := range nums { 658 | s.Add(v) 659 | } 660 | 661 | b.ResetTimer() 662 | for i := 0; i < b.N; i++ { 663 | c := s.Iter() 664 | for range c { 665 | 666 | } 667 | } 668 | } 669 | 670 | func BenchmarkIter1Safe(b *testing.B) { 671 | benchIter(b, 1, NewSet[int]()) 672 | } 673 | 674 | func BenchmarkIter1Unsafe(b *testing.B) { 675 | benchIter(b, 1, NewThreadUnsafeSet[int]()) 676 | } 677 | 678 | func BenchmarkIter10Safe(b *testing.B) { 679 | benchIter(b, 10, NewSet[int]()) 680 | } 681 | 682 | func BenchmarkIter10Unsafe(b *testing.B) { 683 | benchIter(b, 10, NewThreadUnsafeSet[int]()) 684 | } 685 | 686 | func BenchmarkIter100Safe(b *testing.B) { 687 | benchIter(b, 100, NewSet[int]()) 688 | } 689 | 690 | func BenchmarkIter100Unsafe(b *testing.B) { 691 | benchIter(b, 100, NewThreadUnsafeSet[int]()) 692 | } 693 | 694 | func benchIterator(b *testing.B, n int, s Set[int]) { 695 | nums := nrand(n) 696 | for _, v := range nums { 697 | s.Add(v) 698 | } 699 | 700 | b.ResetTimer() 701 | for i := 0; i < b.N; i++ { 702 | c := s.Iterator().C 703 | for range c { 704 | 705 | } 706 | } 707 | } 708 | 709 | func BenchmarkIterator1Safe(b *testing.B) { 710 | benchIterator(b, 1, NewSet[int]()) 711 | } 712 | 713 | func BenchmarkIterator1Unsafe(b *testing.B) { 714 | benchIterator(b, 1, NewThreadUnsafeSet[int]()) 715 | } 716 | 717 | func BenchmarkIterator10Safe(b *testing.B) { 718 | benchIterator(b, 10, NewSet[int]()) 719 | } 720 | 721 | func BenchmarkIterator10Unsafe(b *testing.B) { 722 | benchIterator(b, 10, NewThreadUnsafeSet[int]()) 723 | } 724 | 725 | func BenchmarkIterator100Safe(b *testing.B) { 726 | benchIterator(b, 100, NewSet[int]()) 727 | } 728 | 729 | func BenchmarkIterator100Unsafe(b *testing.B) { 730 | benchIterator(b, 100, NewThreadUnsafeSet[int]()) 731 | } 732 | 733 | func benchString(b *testing.B, n int, s Set[int]) { 734 | nums := nrand(n) 735 | for _, v := range nums { 736 | s.Add(v) 737 | } 738 | 739 | b.ResetTimer() 740 | for i := 0; i < b.N; i++ { 741 | _ = s.String() 742 | } 743 | } 744 | 745 | func BenchmarkString1Safe(b *testing.B) { 746 | benchString(b, 1, NewSet[int]()) 747 | } 748 | 749 | func BenchmarkString1Unsafe(b *testing.B) { 750 | benchString(b, 1, NewThreadUnsafeSet[int]()) 751 | } 752 | 753 | func BenchmarkString10Safe(b *testing.B) { 754 | benchString(b, 10, NewSet[int]()) 755 | } 756 | 757 | func BenchmarkString10Unsafe(b *testing.B) { 758 | benchString(b, 10, NewThreadUnsafeSet[int]()) 759 | } 760 | 761 | func BenchmarkString100Safe(b *testing.B) { 762 | benchString(b, 100, NewSet[int]()) 763 | } 764 | 765 | func BenchmarkString100Unsafe(b *testing.B) { 766 | benchString(b, 100, NewThreadUnsafeSet[int]()) 767 | } 768 | 769 | func benchToSlice(b *testing.B, s Set[int]) { 770 | nums := nrand(b.N) 771 | for _, v := range nums { 772 | s.Add(v) 773 | } 774 | 775 | b.ResetTimer() 776 | for i := 0; i < b.N; i++ { 777 | s.ToSlice() 778 | } 779 | } 780 | 781 | func BenchmarkToSliceSafe(b *testing.B) { 782 | benchToSlice(b, NewSet[int]()) 783 | } 784 | 785 | func BenchmarkToSliceUnsafe(b *testing.B) { 786 | benchToSlice(b, NewThreadUnsafeSet[int]()) 787 | } 788 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deckarep/golang-set/v2 2 | 3 | go 1.18 4 | 5 | require go.mongodb.org/mongo-driver v1.17.4 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 3 | go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4= 4 | go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= 5 | go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= 6 | go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= 7 | -------------------------------------------------------------------------------- /iterator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | // Iterator defines an iterator over a Set, its C channel can be used to range over the Set's 29 | // elements. 30 | type Iterator[T comparable] struct { 31 | C <-chan T 32 | stop chan struct{} 33 | } 34 | 35 | // Stop stops the Iterator, no further elements will be received on C, C will be closed. 36 | func (i *Iterator[T]) Stop() { 37 | // Allows for Stop() to be called multiple times 38 | // (close() panics when called on already closed channel) 39 | defer func() { 40 | recover() 41 | }() 42 | 43 | close(i.stop) 44 | 45 | // Exhaust any remaining elements. 46 | for range i.C { 47 | } 48 | } 49 | 50 | // newIterator returns a new Iterator instance together with its item and stop channels. 51 | func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) { 52 | itemChan := make(chan T) 53 | stopChan := make(chan struct{}) 54 | return &Iterator[T]{ 55 | C: itemChan, 56 | stop: stopChan, 57 | }, itemChan, stopChan 58 | } 59 | -------------------------------------------------------------------------------- /iterator_example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "testing" 30 | ) 31 | 32 | type yourType struct { 33 | name string 34 | } 35 | 36 | func Test_ExampleIterator(t *testing.T) { 37 | 38 | s := NewSet( 39 | []*yourType{ 40 | {name: "Alise"}, 41 | {name: "Bob"}, 42 | {name: "John"}, 43 | {name: "Nick"}, 44 | }..., 45 | ) 46 | 47 | var found *yourType 48 | it := s.Iterator() 49 | 50 | for elem := range it.C { 51 | if elem.name == "John" { 52 | found = elem 53 | it.Stop() 54 | } 55 | } 56 | 57 | if found == nil || found.name != "John" { 58 | t.Fatalf("expected iterator to have found `John` record but got nil or something else") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /new_improved.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrybinzo/golang/00a4909883ac9793de8228fc0c25e1081cb5e66e/new_improved.jpeg -------------------------------------------------------------------------------- /set.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | // Package mapset implements a simple and set collection. 27 | // Items stored within it are unordered and unique. It supports 28 | // typical set operations: membership testing, intersection, union, 29 | // difference, symmetric difference and cloning. 30 | // 31 | // Package mapset provides two implementations of the Set 32 | // interface. The default implementation is safe for concurrent 33 | // access, but a non-thread-safe implementation is also provided for 34 | // programs that can benefit from the slight speed improvement and 35 | // that can enforce mutual exclusion through other means. 36 | package mapset 37 | 38 | import "go.mongodb.org/mongo-driver/bson/bsontype" 39 | 40 | // Set is the primary interface provided by the mapset package. It 41 | // represents an unordered set of data and a large number of 42 | // operations that can be applied to that set. 43 | type Set[T comparable] interface { 44 | // Add adds an element to the set. Returns whether 45 | // the item was added. 46 | Add(val T) bool 47 | 48 | // Append multiple elements to the set. Returns 49 | // the number of elements added. 50 | Append(val ...T) int 51 | 52 | // Cardinality returns the number of elements in the set. 53 | Cardinality() int 54 | 55 | // Clear removes all elements from the set, leaving 56 | // the empty set. 57 | Clear() 58 | 59 | // Clone returns a clone of the set using the same 60 | // implementation, duplicating all keys. 61 | Clone() Set[T] 62 | 63 | // Contains returns whether the given items 64 | // are all in the set. 65 | Contains(val ...T) bool 66 | 67 | // ContainsOne returns whether the given item 68 | // is in the set. 69 | // 70 | // Contains may cause the argument to escape to the heap. 71 | // See: https://github.com/deckarep/golang-set/issues/118 72 | ContainsOne(val T) bool 73 | 74 | // ContainsAny returns whether at least one of the 75 | // given items are in the set. 76 | ContainsAny(val ...T) bool 77 | 78 | // ContainsAnyElement returns whether at least one of the 79 | // given element are in the set. 80 | ContainsAnyElement(other Set[T]) bool 81 | 82 | // Difference returns the difference between this set 83 | // and other. The returned set will contain 84 | // all elements of this set that are not also 85 | // elements of other. 86 | // 87 | // Note that the argument to Difference 88 | // must be of the same type as the receiver 89 | // of the method. Otherwise, Difference will 90 | // panic. 91 | Difference(other Set[T]) Set[T] 92 | 93 | // Equal determines if two sets are equal to each 94 | // other. If they have the same cardinality 95 | // and contain the same elements, they are 96 | // considered equal. The order in which 97 | // the elements were added is irrelevant. 98 | // 99 | // Note that the argument to Equal must be 100 | // of the same type as the receiver of the 101 | // method. Otherwise, Equal will panic. 102 | Equal(other Set[T]) bool 103 | 104 | // Intersect returns a new set containing only the elements 105 | // that exist only in both sets. 106 | // 107 | // Note that the argument to Intersect 108 | // must be of the same type as the receiver 109 | // of the method. Otherwise, Intersect will 110 | // panic. 111 | Intersect(other Set[T]) Set[T] 112 | 113 | // IsEmpty determines if there are elements in the set. 114 | IsEmpty() bool 115 | 116 | // IsProperSubset determines if every element in this set is in 117 | // the other set but the two sets are not equal. 118 | // 119 | // Note that the argument to IsProperSubset 120 | // must be of the same type as the receiver 121 | // of the method. Otherwise, IsProperSubset 122 | // will panic. 123 | IsProperSubset(other Set[T]) bool 124 | 125 | // IsProperSuperset determines if every element in the other set 126 | // is in this set but the two sets are not 127 | // equal. 128 | // 129 | // Note that the argument to IsSuperset 130 | // must be of the same type as the receiver 131 | // of the method. Otherwise, IsSuperset will 132 | // panic. 133 | IsProperSuperset(other Set[T]) bool 134 | 135 | // IsSubset determines if every element in this set is in 136 | // the other set. 137 | // 138 | // Note that the argument to IsSubset 139 | // must be of the same type as the receiver 140 | // of the method. Otherwise, IsSubset will 141 | // panic. 142 | IsSubset(other Set[T]) bool 143 | 144 | // IsSuperset determines if every element in the other set 145 | // is in this set. 146 | // 147 | // Note that the argument to IsSuperset 148 | // must be of the same type as the receiver 149 | // of the method. Otherwise, IsSuperset will 150 | // panic. 151 | IsSuperset(other Set[T]) bool 152 | 153 | // Each iterates over elements and executes the passed func against each element. 154 | // If passed func returns true, stop iteration at the time. 155 | Each(func(T) bool) 156 | 157 | // Iter returns a channel of elements that you can 158 | // range over. 159 | Iter() <-chan T 160 | 161 | // Iterator returns an Iterator object that you can 162 | // use to range over the set. 163 | Iterator() *Iterator[T] 164 | 165 | // Remove removes a single element from the set. 166 | Remove(i T) 167 | 168 | // RemoveAll removes multiple elements from the set. 169 | RemoveAll(i ...T) 170 | 171 | // String provides a convenient string representation 172 | // of the current state of the set. 173 | String() string 174 | 175 | // SymmetricDifference returns a new set with all elements which are 176 | // in either this set or the other set but not in both. 177 | // 178 | // Note that the argument to SymmetricDifference 179 | // must be of the same type as the receiver 180 | // of the method. Otherwise, SymmetricDifference 181 | // will panic. 182 | SymmetricDifference(other Set[T]) Set[T] 183 | 184 | // Union returns a new set with all elements in both sets. 185 | // 186 | // Note that the argument to Union must be of the 187 | // same type as the receiver of the method. 188 | // Otherwise, Union will panic. 189 | Union(other Set[T]) Set[T] 190 | 191 | // Pop removes and returns an arbitrary item from the set. 192 | Pop() (T, bool) 193 | 194 | // PopN removes and returns up to n arbitrary items from the set. 195 | // It returns a slice of the removed items and the actual number of items removed. 196 | // If the set is empty or n is less than or equal to 0s, it returns an empty slice and 0. 197 | // If n is greater than the set's size, all items are 198 | PopN(n int) ([]T, int) 199 | 200 | // ToSlice returns the members of the set as a slice. 201 | ToSlice() []T 202 | 203 | // MarshalJSON will marshal the set into a JSON-based representation. 204 | MarshalJSON() ([]byte, error) 205 | 206 | // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. 207 | // For this to work, set subtypes must implement the Marshal/Unmarshal interface. 208 | UnmarshalJSON(b []byte) error 209 | 210 | // MarshalBSONValue will marshal the set into a BSON-based representation. 211 | MarshalBSONValue() (bsontype.Type, []byte, error) 212 | 213 | // UnmarshalBSONValue will unmarshal a BSON-based byte slice into a full Set datastructure. 214 | // For this to work, set subtypes must implement the Marshal/Unmarshal interface. 215 | UnmarshalBSONValue(bt bsontype.Type, b []byte) error 216 | } 217 | 218 | // NewSet creates and returns a new set with the given elements. 219 | // Operations on the resulting set are thread-safe. 220 | func NewSet[T comparable](vals ...T) Set[T] { 221 | s := newThreadSafeSetWithSize[T](len(vals)) 222 | for _, item := range vals { 223 | s.Add(item) 224 | } 225 | return s 226 | } 227 | 228 | // NewSetWithSize creates and returns a reference to an empty set with a specified 229 | // capacity. Operations on the resulting set are thread-safe. 230 | func NewSetWithSize[T comparable](cardinality int) Set[T] { 231 | s := newThreadSafeSetWithSize[T](cardinality) 232 | return s 233 | } 234 | 235 | // NewThreadUnsafeSet creates and returns a new set with the given elements. 236 | // Operations on the resulting set are not thread-safe. 237 | func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] { 238 | s := newThreadUnsafeSetWithSize[T](len(vals)) 239 | for _, item := range vals { 240 | s.Add(item) 241 | } 242 | return s 243 | } 244 | 245 | // NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with 246 | // a specified capacity. Operations on the resulting set are not thread-safe. 247 | func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] { 248 | s := newThreadUnsafeSetWithSize[T](cardinality) 249 | return s 250 | } 251 | 252 | // NewSetFromMapKeys creates and returns a new set with the given keys of the map. 253 | // Operations on the resulting set are thread-safe. 254 | func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { 255 | s := NewSetWithSize[T](len(val)) 256 | 257 | for k := range val { 258 | s.Add(k) 259 | } 260 | 261 | return s 262 | } 263 | 264 | // NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map. 265 | // Operations on the resulting set are not thread-safe. 266 | func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { 267 | s := NewThreadUnsafeSetWithSize[T](len(val)) 268 | 269 | for k := range val { 270 | s.Add(k) 271 | } 272 | 273 | return s 274 | } 275 | 276 | // Elements returns an iterator that yields the elements of the set. Starting 277 | // with Go 1.23, users can use a for loop to iterate over it. 278 | func Elements[T comparable](s Set[T]) func(func(element T) bool) { 279 | return func(yield func(element T) bool) { 280 | s.Each(func(t T) bool { 281 | return !yield(t) 282 | }) 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /set123_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.23 2 | // +build go1.23 3 | 4 | package mapset 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func Test_Elements123(t *testing.T) { 11 | a := NewSet[string]() 12 | 13 | a.Add("Z") 14 | a.Add("Y") 15 | a.Add("X") 16 | a.Add("W") 17 | 18 | b := NewSet[string]() 19 | for elem := range Elements(a) { 20 | b.Add(elem) 21 | } 22 | 23 | if !a.Equal(b) { 24 | t.Error("The sets are not equal after iterating (Each) through the first set") 25 | } 26 | 27 | var count int 28 | for range Elements(a) { 29 | if count == 2 { 30 | break 31 | } 32 | count++ 33 | } 34 | 35 | if count != 2 { 36 | t.Error("Iteration should stop on the way") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /set_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "testing" 30 | ) 31 | 32 | func makeSetInt(ints []int) Set[int] { 33 | s := NewSet[int]() 34 | for _, i := range ints { 35 | s.Add(i) 36 | } 37 | return s 38 | } 39 | 40 | func makeUnsafeSetInt(ints []int) Set[int] { 41 | s := NewThreadUnsafeSet[int]() 42 | for _, i := range ints { 43 | s.Add(i) 44 | } 45 | return s 46 | } 47 | 48 | func makeSetIntWithAppend(ints ...int) Set[int] { 49 | s := NewSet[int]() 50 | s.Append(ints...) 51 | return s 52 | } 53 | 54 | func makeUnsafeSetIntWithAppend(ints ...int) Set[int] { 55 | s := NewThreadUnsafeSet[int]() 56 | s.Append(ints...) 57 | return s 58 | } 59 | 60 | func assertEqual[T comparable](a, b Set[T], t *testing.T) { 61 | if !a.Equal(b) { 62 | t.Errorf("%v != %v\n", a, b) 63 | } 64 | } 65 | 66 | func Test_NewSet(t *testing.T) { 67 | a := NewSet[int]() 68 | if a.Cardinality() != 0 { 69 | t.Error("NewSet should start out as an empty set") 70 | } 71 | 72 | assertEqual(NewSet([]int{}...), NewSet[int](), t) 73 | assertEqual(NewSet([]int{1}...), NewSet(1), t) 74 | assertEqual(NewSet([]int{1, 2}...), NewSet(1, 2), t) 75 | assertEqual(NewSet([]string{"a"}...), NewSet("a"), t) 76 | assertEqual(NewSet([]string{"a", "b"}...), NewSet("a", "b"), t) 77 | } 78 | 79 | func Test_NewUnsafeSet(t *testing.T) { 80 | a := NewThreadUnsafeSet[int]() 81 | 82 | if a.Cardinality() != 0 { 83 | t.Error("NewSet should start out as an empty set") 84 | } 85 | } 86 | 87 | func Test_AddSet(t *testing.T) { 88 | a := makeSetInt([]int{1, 2, 3}) 89 | 90 | if a.Cardinality() != 3 { 91 | t.Error("AddSet does not have a size of 3 even though 3 items were added to a new set") 92 | } 93 | } 94 | 95 | func Test_AddUnsafeSet(t *testing.T) { 96 | a := makeUnsafeSetInt([]int{1, 2, 3}) 97 | 98 | if a.Cardinality() != 3 { 99 | t.Error("AddSet does not have a size of 3 even though 3 items were added to a new set") 100 | } 101 | } 102 | 103 | func Test_AppendSet(t *testing.T) { 104 | a := makeSetIntWithAppend(1, 2, 3) 105 | 106 | if a.Cardinality() != 3 { 107 | t.Error("AppendSet does not have a size of 3 even though 3 items were added to a new set") 108 | } 109 | } 110 | 111 | func Test_AppendUnsafeSet(t *testing.T) { 112 | a := makeUnsafeSetIntWithAppend(1, 2, 3) 113 | 114 | if a.Cardinality() != 3 { 115 | t.Error("AppendSet does not have a size of 3 even though 3 items were added to a new set") 116 | } 117 | } 118 | 119 | func Test_AddSetNoDuplicate(t *testing.T) { 120 | a := makeSetInt([]int{7, 5, 3, 7}) 121 | 122 | if a.Cardinality() != 3 { 123 | t.Error("AddSetNoDuplicate set should have 3 elements since 7 is a duplicate") 124 | } 125 | 126 | if !(a.Contains(7) && a.Contains(5) && a.Contains(3)) { 127 | t.Error("AddSetNoDuplicate set should have a 7, 5, and 3 in it.") 128 | } 129 | } 130 | 131 | func Test_AddUnsafeSetNoDuplicate(t *testing.T) { 132 | a := makeUnsafeSetInt([]int{7, 5, 3, 7}) 133 | 134 | if a.Cardinality() != 3 { 135 | t.Error("AddSetNoDuplicate set should have 3 elements since 7 is a duplicate") 136 | } 137 | 138 | if !(a.Contains(7) && a.Contains(5) && a.Contains(3)) { 139 | t.Error("AddSetNoDuplicate set should have a 7, 5, and 3 in it.") 140 | } 141 | } 142 | 143 | func Test_AppendSetNoDuplicate(t *testing.T) { 144 | a := makeSetIntWithAppend(7, 5, 3, 7) 145 | 146 | if a.Cardinality() != 3 { 147 | t.Error("AppendSetNoDuplicate set should have 3 elements since 7 is a duplicate") 148 | } 149 | 150 | if !(a.Contains(7) && a.Contains(5) && a.Contains(3)) { 151 | t.Error("AppendSetNoDuplicate set should have a 7, 5, and 3 in it.") 152 | } 153 | } 154 | 155 | func Test_AppendUnsafeSetNoDuplicate(t *testing.T) { 156 | a := makeUnsafeSetIntWithAppend(7, 5, 3, 7) 157 | 158 | if a.Cardinality() != 3 { 159 | t.Error("AppendSetNoDuplicate set should have 3 elements since 7 is a duplicate") 160 | } 161 | 162 | if !(a.Contains(7) && a.Contains(5) && a.Contains(3)) { 163 | t.Error("AppendSetNoDuplicate set should have a 7, 5, and 3 in it.") 164 | } 165 | } 166 | 167 | func Test_RemoveSet(t *testing.T) { 168 | a := makeSetInt([]int{6, 3, 1}) 169 | 170 | a.Remove(3) 171 | 172 | if a.Cardinality() != 2 { 173 | t.Error("RemoveSet should only have 2 items in the set") 174 | } 175 | 176 | if !(a.Contains(6) && a.Contains(1)) { 177 | t.Error("RemoveSet should have only items 6 and 1 in the set") 178 | } 179 | 180 | a.Remove(6) 181 | a.Remove(1) 182 | 183 | if a.Cardinality() != 0 { 184 | t.Error("RemoveSet should be an empty set after removing 6 and 1") 185 | } 186 | } 187 | 188 | func Test_RemoveAllSet(t *testing.T) { 189 | a := makeSetInt([]int{6, 3, 1, 8, 9}) 190 | 191 | a.RemoveAll(3, 1) 192 | 193 | if a.Cardinality() != 3 { 194 | t.Error("RemoveAll should only have 2 items in the set") 195 | } 196 | 197 | if !a.Contains(6, 8, 9) { 198 | t.Error("RemoveAll should have only items (6,8,9) in the set") 199 | } 200 | 201 | a.RemoveAll(6, 8, 9) 202 | 203 | if a.Cardinality() != 0 { 204 | t.Error("RemoveSet should be an empty set after removing 6 and 1") 205 | } 206 | } 207 | 208 | func Test_RemoveUnsafeSet(t *testing.T) { 209 | a := makeUnsafeSetInt([]int{6, 3, 1}) 210 | 211 | a.Remove(3) 212 | 213 | if a.Cardinality() != 2 { 214 | t.Error("RemoveSet should only have 2 items in the set") 215 | } 216 | 217 | if !(a.Contains(6) && a.Contains(1)) { 218 | t.Error("RemoveSet should have only items 6 and 1 in the set") 219 | } 220 | 221 | a.Remove(6) 222 | a.Remove(1) 223 | 224 | if a.Cardinality() != 0 { 225 | t.Error("RemoveSet should be an empty set after removing 6 and 1") 226 | } 227 | } 228 | 229 | func Test_RemoveAllUnsafeSet(t *testing.T) { 230 | a := makeUnsafeSetInt([]int{6, 3, 1, 8, 9}) 231 | 232 | a.RemoveAll(3, 1) 233 | 234 | if a.Cardinality() != 3 { 235 | t.Error("RemoveAll should only have 2 items in the set") 236 | } 237 | 238 | if !a.Contains(6, 8, 9) { 239 | t.Error("RemoveAll should have only items (6,8,9) in the set") 240 | } 241 | 242 | a.RemoveAll(6, 8, 9) 243 | 244 | if a.Cardinality() != 0 { 245 | t.Error("RemoveSet should be an empty set after removing 6 and 1") 246 | } 247 | } 248 | 249 | func Test_ContainsSet(t *testing.T) { 250 | a := NewSet[int]() 251 | 252 | a.Add(71) 253 | 254 | if !a.Contains(71) { 255 | t.Error("ContainsSet should contain 71") 256 | } 257 | 258 | a.Remove(71) 259 | 260 | if a.Contains(71) { 261 | t.Error("ContainsSet should not contain 71") 262 | } 263 | 264 | a.Add(13) 265 | a.Add(7) 266 | a.Add(1) 267 | 268 | if !(a.Contains(13) && a.Contains(7) && a.Contains(1)) { 269 | t.Error("ContainsSet should contain 13, 7, 1") 270 | } 271 | } 272 | 273 | func Test_ContainsUnsafeSet(t *testing.T) { 274 | a := NewThreadUnsafeSet[int]() 275 | 276 | a.Add(71) 277 | 278 | if !a.Contains(71) { 279 | t.Error("ContainsSet should contain 71") 280 | } 281 | 282 | a.Remove(71) 283 | 284 | if a.Contains(71) { 285 | t.Error("ContainsSet should not contain 71") 286 | } 287 | 288 | a.Add(13) 289 | a.Add(7) 290 | a.Add(1) 291 | 292 | if !(a.Contains(13) && a.Contains(7) && a.Contains(1)) { 293 | t.Error("ContainsSet should contain 13, 7, 1") 294 | } 295 | } 296 | 297 | func Test_ContainsMultipleSet(t *testing.T) { 298 | a := makeSetInt([]int{8, 6, 7, 5, 3, 0, 9}) 299 | 300 | if !a.Contains(8, 6, 7, 5, 3, 0, 9) { 301 | t.Error("ContainsAll should contain Jenny's phone number") 302 | } 303 | 304 | if a.Contains(8, 6, 11, 5, 3, 0, 9) { 305 | t.Error("ContainsAll should not have all of these numbers") 306 | } 307 | } 308 | 309 | func Test_ContainsMultipleUnsafeSet(t *testing.T) { 310 | a := makeUnsafeSetInt([]int{8, 6, 7, 5, 3, 0, 9}) 311 | 312 | if !a.Contains(8, 6, 7, 5, 3, 0, 9) { 313 | t.Error("ContainsAll should contain Jenny's phone number") 314 | } 315 | 316 | if a.Contains(8, 6, 11, 5, 3, 0, 9) { 317 | t.Error("ContainsAll should not have all of these numbers") 318 | } 319 | } 320 | 321 | func Test_ContainsOneSet(t *testing.T) { 322 | a := NewSet[int]() 323 | 324 | a.Add(71) 325 | 326 | if !a.ContainsOne(71) { 327 | t.Error("ContainsSet should contain 71") 328 | } 329 | 330 | a.Remove(71) 331 | 332 | if a.ContainsOne(71) { 333 | t.Error("ContainsSet should not contain 71") 334 | } 335 | 336 | a.Add(13) 337 | a.Add(7) 338 | a.Add(1) 339 | 340 | if !(a.ContainsOne(13) && a.ContainsOne(7) && a.ContainsOne(1)) { 341 | t.Error("ContainsSet should contain 13, 7, 1") 342 | } 343 | } 344 | 345 | func Test_ContainsOneUnsafeSet(t *testing.T) { 346 | a := NewThreadUnsafeSet[int]() 347 | 348 | a.Add(71) 349 | 350 | if !a.ContainsOne(71) { 351 | t.Error("ContainsSet should contain 71") 352 | } 353 | 354 | a.Remove(71) 355 | 356 | if a.ContainsOne(71) { 357 | t.Error("ContainsSet should not contain 71") 358 | } 359 | 360 | a.Add(13) 361 | a.Add(7) 362 | a.Add(1) 363 | 364 | if !(a.ContainsOne(13) && a.ContainsOne(7) && a.ContainsOne(1)) { 365 | t.Error("ContainsSet should contain 13, 7, 1") 366 | } 367 | } 368 | 369 | func Test_ContainsAnySet(t *testing.T) { 370 | a := NewSet[int]() 371 | 372 | a.Add(71) 373 | 374 | if !a.ContainsAny(71) { 375 | t.Error("ContainsSet should contain 71") 376 | } 377 | 378 | if !a.ContainsAny(71, 10) { 379 | t.Error("ContainsSet should contain 71 or 10") 380 | } 381 | 382 | a.Remove(71) 383 | 384 | if a.ContainsAny(71) { 385 | t.Error("ContainsSet should not contain 71") 386 | } 387 | 388 | if a.ContainsAny(71, 10) { 389 | t.Error("ContainsSet should not contain 71 or 10") 390 | } 391 | 392 | a.Add(13) 393 | a.Add(7) 394 | a.Add(1) 395 | 396 | if !(a.ContainsAny(13, 17, 10)) { 397 | t.Error("ContainsSet should contain 13, 17, or 10") 398 | } 399 | } 400 | 401 | func Test_ContainsAnyElement(t *testing.T) { 402 | a := NewSet[int]() 403 | a.Add(1) 404 | a.Add(3) 405 | a.Add(5) 406 | 407 | b := NewSet[int]() 408 | a.Add(2) 409 | a.Add(4) 410 | a.Add(6) 411 | 412 | if ret := a.ContainsAnyElement(b); ret { 413 | t.Errorf("set a not contain any element in set b") 414 | } 415 | 416 | a.Add(10) 417 | 418 | if ret := a.ContainsAnyElement(b); ret { 419 | t.Errorf("set a not contain any element in set b") 420 | } 421 | 422 | b.Add(10) 423 | 424 | if ret := a.ContainsAnyElement(b); !ret { 425 | t.Errorf("set a contain 10") 426 | } 427 | } 428 | func Test_ClearSet(t *testing.T) { 429 | a := makeSetInt([]int{2, 5, 9, 10}) 430 | 431 | a.Clear() 432 | 433 | if a.Cardinality() != 0 { 434 | t.Error("ClearSet should be an empty set") 435 | } 436 | } 437 | 438 | func Test_ClearUnsafeSet(t *testing.T) { 439 | a := makeUnsafeSetInt([]int{2, 5, 9, 10}) 440 | 441 | a.Clear() 442 | 443 | if a.Cardinality() != 0 { 444 | t.Error("ClearSet should be an empty set") 445 | } 446 | } 447 | 448 | func Test_CardinalitySet(t *testing.T) { 449 | a := NewSet[int]() 450 | 451 | if a.Cardinality() != 0 { 452 | t.Error("set should be an empty set") 453 | } 454 | 455 | a.Add(1) 456 | 457 | if a.Cardinality() != 1 { 458 | t.Error("set should have a size of 1") 459 | } 460 | 461 | a.Remove(1) 462 | 463 | if a.Cardinality() != 0 { 464 | t.Error("set should be an empty set") 465 | } 466 | 467 | a.Add(9) 468 | 469 | if a.Cardinality() != 1 { 470 | t.Error("set should have a size of 1") 471 | } 472 | 473 | a.Clear() 474 | 475 | if a.Cardinality() != 0 { 476 | t.Error("set should have a size of 1") 477 | } 478 | } 479 | 480 | func Test_CardinalityUnsafeSet(t *testing.T) { 481 | a := NewThreadUnsafeSet[int]() 482 | 483 | if a.Cardinality() != 0 { 484 | t.Error("set should be an empty set") 485 | } 486 | 487 | a.Add(1) 488 | 489 | if a.Cardinality() != 1 { 490 | t.Error("set should have a size of 1") 491 | } 492 | 493 | a.Remove(1) 494 | 495 | if a.Cardinality() != 0 { 496 | t.Error("set should be an empty set") 497 | } 498 | 499 | a.Add(9) 500 | 501 | if a.Cardinality() != 1 { 502 | t.Error("set should have a size of 1") 503 | } 504 | 505 | a.Clear() 506 | 507 | if a.Cardinality() != 0 { 508 | t.Error("set should have a size of 1") 509 | } 510 | } 511 | 512 | func Test_SetIsSubset(t *testing.T) { 513 | a := makeSetInt([]int{1, 2, 3, 5, 7}) 514 | 515 | b := NewSet[int]() 516 | b.Add(3) 517 | b.Add(5) 518 | b.Add(7) 519 | 520 | if !b.IsSubset(a) { 521 | t.Error("set b should be a subset of set a") 522 | } 523 | 524 | b.Add(72) 525 | 526 | if b.IsSubset(a) { 527 | t.Error("set b should not be a subset of set a because it contains 72 which is not in the set of a") 528 | } 529 | } 530 | 531 | func Test_SetIsProperSubset(t *testing.T) { 532 | a := makeSetInt([]int{1, 2, 3, 5, 7}) 533 | b := makeSetInt([]int{7, 5, 3, 2, 1}) 534 | 535 | if !a.IsSubset(b) { 536 | t.Error("set a should be a subset of set b") 537 | } 538 | if a.IsProperSubset(b) { 539 | t.Error("set a should not be a proper subset of set b (they're equal)") 540 | } 541 | 542 | b.Add(72) 543 | 544 | if !a.IsSubset(b) { 545 | t.Error("set a should be a subset of set b") 546 | } 547 | if !a.IsProperSubset(b) { 548 | t.Error("set a should be a proper subset of set b") 549 | } 550 | } 551 | 552 | func Test_UnsafeSetIsSubset(t *testing.T) { 553 | a := makeUnsafeSetInt([]int{1, 2, 3, 5, 7}) 554 | 555 | b := NewThreadUnsafeSet[int]() 556 | b.Add(3) 557 | b.Add(5) 558 | b.Add(7) 559 | 560 | if !b.IsSubset(a) { 561 | t.Error("set b should be a subset of set a") 562 | } 563 | 564 | b.Add(72) 565 | 566 | if b.IsSubset(a) { 567 | t.Error("set b should not be a subset of set a because it contains 72 which is not in the set of a") 568 | } 569 | } 570 | 571 | func Test_UnsafeSetIsProperSubset(t *testing.T) { 572 | a := makeUnsafeSetInt([]int{1, 2, 3, 5, 7}) 573 | b := NewThreadUnsafeSet[int]() 574 | b.Add(7) 575 | b.Add(1) 576 | b.Add(5) 577 | b.Add(3) 578 | b.Add(2) 579 | 580 | if !a.IsSubset(b) { 581 | t.Error("set a should be a subset of set b") 582 | } 583 | if a.IsProperSubset(b) { 584 | t.Error("set a should not be a proper subset of set b (they're equal)") 585 | } 586 | 587 | b.Add(72) 588 | 589 | if !a.IsSubset(b) { 590 | t.Error("set a should be a subset of set b") 591 | } 592 | if !a.IsProperSubset(b) { 593 | t.Error("set a should be a proper subset of set b because set b has 72") 594 | } 595 | } 596 | 597 | func Test_SetIsSuperset(t *testing.T) { 598 | a := NewSet[int]() 599 | a.Add(9) 600 | a.Add(5) 601 | a.Add(2) 602 | a.Add(1) 603 | a.Add(11) 604 | 605 | b := NewSet[int]() 606 | b.Add(5) 607 | b.Add(2) 608 | b.Add(11) 609 | 610 | if !a.IsSuperset(b) { 611 | t.Error("set a should be a superset of set b") 612 | } 613 | 614 | b.Add(42) 615 | 616 | if a.IsSuperset(b) { 617 | t.Error("set a should not be a superset of set b because set b has a 42") 618 | } 619 | } 620 | 621 | func Test_SetIsProperSuperset(t *testing.T) { 622 | a := NewSet[int]() 623 | a.Add(5) 624 | a.Add(2) 625 | a.Add(11) 626 | 627 | b := NewSet[int]() 628 | b.Add(2) 629 | b.Add(5) 630 | b.Add(11) 631 | 632 | if !a.IsSuperset(b) { 633 | t.Error("set a should be a superset of set b") 634 | } 635 | if a.IsProperSuperset(b) { 636 | t.Error("set a should not be a proper superset of set b (they're equal)") 637 | } 638 | 639 | a.Add(9) 640 | 641 | if !a.IsSuperset(b) { 642 | t.Error("set a should be a superset of set b") 643 | } 644 | if !a.IsProperSuperset(b) { 645 | t.Error("set a not be a proper superset of set b because set a has a 9") 646 | } 647 | 648 | b.Add(42) 649 | 650 | if a.IsSuperset(b) { 651 | t.Error("set a should not be a superset of set b because set b has a 42") 652 | } 653 | if a.IsProperSuperset(b) { 654 | t.Error("set a should not be a proper superset of set b because set b has a 42") 655 | } 656 | } 657 | 658 | func Test_UnsafeSetIsSuperset(t *testing.T) { 659 | a := NewThreadUnsafeSet[int]() 660 | a.Add(9) 661 | a.Add(5) 662 | a.Add(2) 663 | a.Add(1) 664 | a.Add(11) 665 | 666 | b := NewThreadUnsafeSet[int]() 667 | b.Add(5) 668 | b.Add(2) 669 | b.Add(11) 670 | 671 | if !a.IsSuperset(b) { 672 | t.Error("set a should be a superset of set b") 673 | } 674 | 675 | b.Add(42) 676 | 677 | if a.IsSuperset(b) { 678 | t.Error("set a should not be a superset of set b because set a has a 42") 679 | } 680 | } 681 | 682 | func Test_UnsafeSetIsProperSuperset(t *testing.T) { 683 | a := NewThreadUnsafeSet[int]() 684 | a.Add(5) 685 | a.Add(2) 686 | a.Add(11) 687 | 688 | b := NewThreadUnsafeSet[int]() 689 | b.Add(2) 690 | b.Add(5) 691 | b.Add(11) 692 | 693 | if !a.IsSuperset(b) { 694 | t.Error("set a should be a superset of set b") 695 | } 696 | if a.IsProperSuperset(b) { 697 | t.Error("set a should not be a proper superset of set b (they're equal)") 698 | } 699 | 700 | a.Add(9) 701 | 702 | if !a.IsSuperset(b) { 703 | t.Error("set a should be a superset of set b") 704 | } 705 | if !a.IsProperSuperset(b) { 706 | t.Error("set a not be a proper superset of set b because set a has a 9") 707 | } 708 | 709 | b.Add(42) 710 | 711 | if a.IsSuperset(b) { 712 | t.Error("set a should not be a superset of set b because set b has a 42") 713 | } 714 | if a.IsProperSuperset(b) { 715 | t.Error("set a should not be a proper superset of set b because set b has a 42") 716 | } 717 | } 718 | 719 | func Test_SetUnion(t *testing.T) { 720 | a := NewSet[int]() 721 | 722 | b := NewSet[int]() 723 | b.Add(1) 724 | b.Add(2) 725 | b.Add(3) 726 | b.Add(4) 727 | b.Add(5) 728 | 729 | c := a.Union(b) 730 | 731 | if c.Cardinality() != 5 { 732 | t.Error("set c is unioned with an empty set and therefore should have 5 elements in it") 733 | } 734 | 735 | d := NewSet[int]() 736 | d.Add(10) 737 | d.Add(14) 738 | d.Add(0) 739 | 740 | e := c.Union(d) 741 | if e.Cardinality() != 8 { 742 | t.Error("set e should have 8 elements in it after being unioned with set c to d") 743 | } 744 | 745 | f := NewSet[int]() 746 | f.Add(14) 747 | f.Add(3) 748 | 749 | g := f.Union(e) 750 | if g.Cardinality() != 8 { 751 | t.Error("set g should still have 8 elements in it after being unioned with set f that has duplicates") 752 | } 753 | } 754 | 755 | func Test_UnsafeSetUnion(t *testing.T) { 756 | a := NewThreadUnsafeSet[int]() 757 | 758 | b := NewThreadUnsafeSet[int]() 759 | b.Add(1) 760 | b.Add(2) 761 | b.Add(3) 762 | b.Add(4) 763 | b.Add(5) 764 | 765 | c := a.Union(b) 766 | 767 | if c.Cardinality() != 5 { 768 | t.Error("set c is unioned with an empty set and therefore should have 5 elements in it") 769 | } 770 | 771 | d := NewThreadUnsafeSet[int]() 772 | d.Add(10) 773 | d.Add(14) 774 | d.Add(0) 775 | 776 | e := c.Union(d) 777 | if e.Cardinality() != 8 { 778 | t.Error("set e should have 8 elements in it after being unioned with set c to d") 779 | } 780 | 781 | f := NewThreadUnsafeSet[int]() 782 | f.Add(14) 783 | f.Add(3) 784 | 785 | g := f.Union(e) 786 | if g.Cardinality() != 8 { 787 | t.Error("set g should still have 8 elements in it after being unioned with set f that has duplicates") 788 | } 789 | } 790 | 791 | func Test_SetIntersect(t *testing.T) { 792 | a := NewSet[int]() 793 | a.Add(1) 794 | a.Add(3) 795 | a.Add(5) 796 | 797 | b := NewSet[int]() 798 | a.Add(2) 799 | a.Add(4) 800 | a.Add(6) 801 | 802 | c := a.Intersect(b) 803 | 804 | if c.Cardinality() != 0 { 805 | t.Error("set c should be the empty set because there is no common items to intersect") 806 | } 807 | 808 | a.Add(10) 809 | b.Add(10) 810 | 811 | d := a.Intersect(b) 812 | 813 | if !(d.Cardinality() == 1 && d.Contains(10)) { 814 | t.Error("set d should have a size of 1 and contain the item 10") 815 | } 816 | } 817 | 818 | func Test_UnsafeSetIntersect(t *testing.T) { 819 | a := NewThreadUnsafeSet[int]() 820 | a.Add(1) 821 | a.Add(3) 822 | a.Add(5) 823 | 824 | b := NewThreadUnsafeSet[int]() 825 | a.Add(2) 826 | a.Add(4) 827 | a.Add(6) 828 | 829 | c := a.Intersect(b) 830 | 831 | if c.Cardinality() != 0 { 832 | t.Error("set c should be the empty set because there is no common items to intersect") 833 | } 834 | 835 | a.Add(10) 836 | b.Add(10) 837 | 838 | d := a.Intersect(b) 839 | 840 | if !(d.Cardinality() == 1 && d.Contains(10)) { 841 | t.Error("set d should have a size of 1 and contain the item 10") 842 | } 843 | } 844 | 845 | func Test_SetDifference(t *testing.T) { 846 | a := NewSet[int]() 847 | a.Add(1) 848 | a.Add(2) 849 | a.Add(3) 850 | 851 | b := NewSet[int]() 852 | b.Add(1) 853 | b.Add(3) 854 | b.Add(4) 855 | b.Add(5) 856 | b.Add(6) 857 | b.Add(99) 858 | 859 | c := a.Difference(b) 860 | 861 | if !(c.Cardinality() == 1 && c.Contains(2)) { 862 | t.Error("the difference of set a to b is the set of 1 item: 2") 863 | } 864 | } 865 | 866 | func Test_UnsafeSetDifference(t *testing.T) { 867 | a := NewThreadUnsafeSet[int]() 868 | a.Add(1) 869 | a.Add(2) 870 | a.Add(3) 871 | 872 | b := NewThreadUnsafeSet[int]() 873 | b.Add(1) 874 | b.Add(3) 875 | b.Add(4) 876 | b.Add(5) 877 | b.Add(6) 878 | b.Add(99) 879 | 880 | c := a.Difference(b) 881 | 882 | if !(c.Cardinality() == 1 && c.Contains(2)) { 883 | t.Error("the difference of set a to b is the set of 1 item: 2") 884 | } 885 | } 886 | 887 | func Test_SetSymmetricDifference(t *testing.T) { 888 | a := NewSet[int]() 889 | a.Add(1) 890 | a.Add(2) 891 | a.Add(3) 892 | a.Add(45) 893 | 894 | b := NewSet[int]() 895 | b.Add(1) 896 | b.Add(3) 897 | b.Add(4) 898 | b.Add(5) 899 | b.Add(6) 900 | b.Add(99) 901 | 902 | c := a.SymmetricDifference(b) 903 | 904 | if !(c.Cardinality() == 6 && c.Contains(2) && c.Contains(45) && c.Contains(4) && c.Contains(5) && c.Contains(6) && c.Contains(99)) { 905 | t.Error("the symmetric difference of set a to b is the set of 6 items: 2, 45, 4, 5, 6, 99") 906 | } 907 | } 908 | 909 | func Test_UnsafeSetSymmetricDifference(t *testing.T) { 910 | a := NewThreadUnsafeSet[int]() 911 | a.Add(1) 912 | a.Add(2) 913 | a.Add(3) 914 | a.Add(45) 915 | 916 | b := NewThreadUnsafeSet[int]() 917 | b.Add(1) 918 | b.Add(3) 919 | b.Add(4) 920 | b.Add(5) 921 | b.Add(6) 922 | b.Add(99) 923 | 924 | c := a.SymmetricDifference(b) 925 | 926 | if !(c.Cardinality() == 6 && c.Contains(2) && c.Contains(45) && c.Contains(4) && c.Contains(5) && c.Contains(6) && c.Contains(99)) { 927 | t.Error("the symmetric difference of set a to b is the set of 6 items: 2, 45, 4, 5, 6, 99") 928 | } 929 | } 930 | 931 | func Test_SetEqual(t *testing.T) { 932 | a := NewSet[int]() 933 | b := NewSet[int]() 934 | 935 | if !a.Equal(b) { 936 | t.Error("Both a and b are empty sets, and should be equal") 937 | } 938 | 939 | a.Add(10) 940 | 941 | if a.Equal(b) { 942 | t.Error("a should not be equal to b because b is empty and a has item 1 in it") 943 | } 944 | 945 | b.Add(10) 946 | 947 | if !a.Equal(b) { 948 | t.Error("a is now equal again to b because both have the item 10 in them") 949 | } 950 | 951 | b.Add(8) 952 | b.Add(3) 953 | b.Add(47) 954 | 955 | if a.Equal(b) { 956 | t.Error("b has 3 more elements in it so therefore should not be equal to a") 957 | } 958 | 959 | a.Add(8) 960 | a.Add(3) 961 | a.Add(47) 962 | 963 | if !a.Equal(b) { 964 | t.Error("a and b should be equal with the same number of elements") 965 | } 966 | } 967 | 968 | func Test_UnsafeSetEqual(t *testing.T) { 969 | a := NewThreadUnsafeSet[int]() 970 | b := NewThreadUnsafeSet[int]() 971 | 972 | if !a.Equal(b) { 973 | t.Error("Both a and b are empty sets, and should be equal") 974 | } 975 | 976 | a.Add(10) 977 | 978 | if a.Equal(b) { 979 | t.Error("a should not be equal to b because b is empty and a has item 1 in it") 980 | } 981 | 982 | b.Add(10) 983 | 984 | if !a.Equal(b) { 985 | t.Error("a is now equal again to b because both have the item 10 in them") 986 | } 987 | 988 | b.Add(8) 989 | b.Add(3) 990 | b.Add(47) 991 | 992 | if a.Equal(b) { 993 | t.Error("b has 3 more elements in it so therefore should not be equal to a") 994 | } 995 | 996 | a.Add(8) 997 | a.Add(3) 998 | a.Add(47) 999 | 1000 | if !a.Equal(b) { 1001 | t.Error("a and b should be equal with the same number of elements") 1002 | } 1003 | } 1004 | 1005 | func Test_SetClone(t *testing.T) { 1006 | a := NewSet[int]() 1007 | a.Add(1) 1008 | a.Add(2) 1009 | 1010 | b := a.Clone() 1011 | 1012 | if !a.Equal(b) { 1013 | t.Error("Clones should be equal") 1014 | } 1015 | 1016 | a.Add(3) 1017 | if a.Equal(b) { 1018 | t.Error("a contains one more element, they should not be equal") 1019 | } 1020 | 1021 | c := a.Clone() 1022 | c.Remove(1) 1023 | 1024 | if a.Equal(c) { 1025 | t.Error("C contains one element less, they should not be equal") 1026 | } 1027 | } 1028 | 1029 | func Test_UnsafeSetClone(t *testing.T) { 1030 | a := NewThreadUnsafeSet[int]() 1031 | a.Add(1) 1032 | a.Add(2) 1033 | 1034 | b := a.Clone() 1035 | 1036 | if !a.Equal(b) { 1037 | t.Error("Clones should be equal") 1038 | } 1039 | 1040 | a.Add(3) 1041 | if a.Equal(b) { 1042 | t.Error("a contains one more element, they should not be equal") 1043 | } 1044 | 1045 | c := a.Clone() 1046 | c.Remove(1) 1047 | 1048 | if a.Equal(c) { 1049 | t.Error("C contains one element less, they should not be equal") 1050 | } 1051 | } 1052 | 1053 | func Test_Each(t *testing.T) { 1054 | a := NewSet[string]() 1055 | 1056 | a.Add("Z") 1057 | a.Add("Y") 1058 | a.Add("X") 1059 | a.Add("W") 1060 | 1061 | b := NewSet[string]() 1062 | a.Each(func(elem string) bool { 1063 | b.Add(elem) 1064 | return false 1065 | }) 1066 | 1067 | if !a.Equal(b) { 1068 | t.Error("The sets are not equal after iterating (Each) through the first set") 1069 | } 1070 | 1071 | var count int 1072 | a.Each(func(elem string) bool { 1073 | if count == 2 { 1074 | return true 1075 | } 1076 | count++ 1077 | return false 1078 | }) 1079 | if count != 2 { 1080 | t.Error("Iteration should stop on the way") 1081 | } 1082 | } 1083 | 1084 | func Test_Iter(t *testing.T) { 1085 | a := NewSet[string]() 1086 | 1087 | a.Add("Z") 1088 | a.Add("Y") 1089 | a.Add("X") 1090 | a.Add("W") 1091 | 1092 | b := NewSet[string]() 1093 | for val := range a.Iter() { 1094 | b.Add(val) 1095 | } 1096 | 1097 | if !a.Equal(b) { 1098 | t.Error("The sets are not equal after iterating (Iter) through the first set") 1099 | } 1100 | } 1101 | 1102 | func Test_UnsafeIter(t *testing.T) { 1103 | a := NewThreadUnsafeSet[string]() 1104 | 1105 | a.Add("Z") 1106 | a.Add("Y") 1107 | a.Add("X") 1108 | a.Add("W") 1109 | 1110 | b := NewThreadUnsafeSet[string]() 1111 | for val := range a.Iter() { 1112 | b.Add(val) 1113 | } 1114 | 1115 | if !a.Equal(b) { 1116 | t.Error("The sets are not equal after iterating (Iter) through the first set") 1117 | } 1118 | } 1119 | 1120 | func Test_Iterator(t *testing.T) { 1121 | a := NewSet[string]() 1122 | 1123 | a.Add("Z") 1124 | a.Add("Y") 1125 | a.Add("X") 1126 | a.Add("W") 1127 | 1128 | b := NewSet[string]() 1129 | for val := range a.Iterator().C { 1130 | b.Add(val) 1131 | } 1132 | 1133 | if !a.Equal(b) { 1134 | t.Error("The sets are not equal after iterating (Iterator) through the first set") 1135 | } 1136 | } 1137 | 1138 | func Test_UnsafeIterator(t *testing.T) { 1139 | a := NewThreadUnsafeSet[string]() 1140 | 1141 | a.Add("Z") 1142 | a.Add("Y") 1143 | a.Add("X") 1144 | a.Add("W") 1145 | 1146 | b := NewThreadUnsafeSet[string]() 1147 | for val := range a.Iterator().C { 1148 | b.Add(val) 1149 | } 1150 | 1151 | if !a.Equal(b) { 1152 | t.Error("The sets are not equal after iterating (Iterator) through the first set") 1153 | } 1154 | } 1155 | 1156 | func Test_IteratorStop(t *testing.T) { 1157 | a := NewSet[string]() 1158 | 1159 | a.Add("Z") 1160 | a.Add("Y") 1161 | a.Add("X") 1162 | a.Add("W") 1163 | 1164 | it := a.Iterator() 1165 | it.Stop() 1166 | for range it.C { 1167 | t.Error("The iterating (Iterator) did not stop after Stop() has been called") 1168 | } 1169 | } 1170 | 1171 | func Test_PopSafe(t *testing.T) { 1172 | a := NewSet[string]() 1173 | 1174 | a.Add("a") 1175 | a.Add("b") 1176 | a.Add("c") 1177 | a.Add("d") 1178 | 1179 | aPop := func() (v string) { 1180 | v, _ = a.Pop() 1181 | return 1182 | } 1183 | 1184 | captureSet := NewSet[string]() 1185 | captureSet.Add(aPop()) 1186 | captureSet.Add(aPop()) 1187 | captureSet.Add(aPop()) 1188 | captureSet.Add(aPop()) 1189 | finalNil := aPop() 1190 | 1191 | if captureSet.Cardinality() != 4 { 1192 | t.Error("unexpected captureSet cardinality; should be 4") 1193 | } 1194 | 1195 | if a.Cardinality() != 0 { 1196 | t.Error("unepxected a cardinality; should be zero") 1197 | } 1198 | 1199 | if !captureSet.Contains("c", "a", "d", "b") { 1200 | t.Error("unexpected result set; should be a,b,c,d (any order is fine") 1201 | } 1202 | 1203 | if finalNil != "" { 1204 | t.Error("when original set is empty, further pops should result in nil") 1205 | } 1206 | } 1207 | 1208 | func Test_PopUnsafe(t *testing.T) { 1209 | a := NewThreadUnsafeSet[string]() 1210 | 1211 | a.Add("a") 1212 | a.Add("b") 1213 | a.Add("c") 1214 | a.Add("d") 1215 | 1216 | aPop := func() (v string) { 1217 | v, _ = a.Pop() 1218 | return 1219 | } 1220 | 1221 | captureSet := NewThreadUnsafeSet[string]() 1222 | captureSet.Add(aPop()) 1223 | captureSet.Add(aPop()) 1224 | captureSet.Add(aPop()) 1225 | captureSet.Add(aPop()) 1226 | finalNil := aPop() 1227 | 1228 | if captureSet.Cardinality() != 4 { 1229 | t.Error("unexpected captureSet cardinality; should be 4") 1230 | } 1231 | 1232 | if a.Cardinality() != 0 { 1233 | t.Error("unepxected a cardinality; should be zero") 1234 | } 1235 | 1236 | if !captureSet.Contains("c", "a", "d", "b") { 1237 | t.Error("unexpected result set; should be a,b,c,d (any order is fine") 1238 | } 1239 | 1240 | if finalNil != "" { 1241 | t.Error("when original set is empty, further pops should result in nil") 1242 | } 1243 | } 1244 | 1245 | func Test_PopNSafe(t *testing.T) { 1246 | a := NewSet[string]() 1247 | a.Add("a") 1248 | a.Add("b") 1249 | a.Add("c") 1250 | a.Add("d") 1251 | 1252 | // Test pop with n <= 0 1253 | items, count := a.PopN(0) 1254 | if count != 0 { 1255 | t.Errorf("expected 0 items popped, got %d", count) 1256 | } 1257 | items, count = a.PopN(-1) 1258 | if count != 0 { 1259 | t.Errorf("expected 0 items popped, got %d", count) 1260 | } 1261 | 1262 | captureSet := NewSet[string]() 1263 | 1264 | // Test pop 2 items 1265 | items, count = a.PopN(2) 1266 | if count != 2 { 1267 | t.Errorf("expected 2 items popped, got %d", count) 1268 | } 1269 | if len(items) != 2 { 1270 | t.Errorf("expected 2 items in slice, got %d", len(items)) 1271 | } 1272 | if a.Cardinality() != 2 { 1273 | t.Errorf("expected 2 items remaining, got %d", a.Cardinality()) 1274 | } 1275 | captureSet.Append(items...) 1276 | 1277 | // Test pop more than remaining 1278 | items, count = a.PopN(3) 1279 | if count != 2 { 1280 | t.Errorf("expected 2 items popped, got %d", count) 1281 | } 1282 | if a.Cardinality() != 0 { 1283 | t.Errorf("expected 0 items remaining, got %d", a.Cardinality()) 1284 | } 1285 | captureSet.Append(items...) 1286 | 1287 | // Test pop from empty set 1288 | items, count = a.PopN(1) 1289 | if count != 0 { 1290 | t.Errorf("expected 0 items popped, got %d", count) 1291 | } 1292 | if len(items) != 0 { 1293 | t.Errorf("expected empty slice, got %d items", len(items)) 1294 | } 1295 | 1296 | if !captureSet.Contains("c", "a", "d", "b") { 1297 | t.Error("unexpected result set; should be a,b,c,d (any order is fine") 1298 | } 1299 | } 1300 | 1301 | func Test_PopNUnsafe(t *testing.T) { 1302 | a := NewThreadUnsafeSet[string]() 1303 | a.Add("a") 1304 | a.Add("b") 1305 | a.Add("c") 1306 | a.Add("d") 1307 | 1308 | // Test pop with n <= 0 1309 | items, count := a.PopN(0) 1310 | if count != 0 { 1311 | t.Errorf("expected 0 items popped, got %d", count) 1312 | } 1313 | items, count = a.PopN(-1) 1314 | if count != 0 { 1315 | t.Errorf("expected 0 items popped, got %d", count) 1316 | } 1317 | 1318 | captureSet := NewThreadUnsafeSet[string]() 1319 | 1320 | // Test pop 2 items 1321 | items, count = a.PopN(2) 1322 | if count != 2 { 1323 | t.Errorf("expected 2 items popped, got %d", count) 1324 | } 1325 | if len(items) != 2 { 1326 | t.Errorf("expected 2 items in slice, got %d", len(items)) 1327 | } 1328 | if a.Cardinality() != 2 { 1329 | t.Errorf("expected 2 items remaining, got %d", a.Cardinality()) 1330 | } 1331 | captureSet.Append(items...) 1332 | 1333 | // Test pop more than remaining 1334 | items, count = a.PopN(3) 1335 | if count != 2 { 1336 | t.Errorf("expected 2 items popped, got %d", count) 1337 | } 1338 | if a.Cardinality() != 0 { 1339 | t.Errorf("expected 0 items remaining, got %d", a.Cardinality()) 1340 | } 1341 | captureSet.Append(items...) 1342 | 1343 | // Test pop from empty set 1344 | items, count = a.PopN(1) 1345 | if count != 0 { 1346 | t.Errorf("expected 0 items popped, got %d", count) 1347 | } 1348 | if len(items) != 0 { 1349 | t.Errorf("expected empty slice, got %d items", len(items)) 1350 | } 1351 | 1352 | if !captureSet.Contains("c", "a", "d", "b") { 1353 | t.Error("unexpected result set; should be a,b,c,d (any order is fine") 1354 | } 1355 | } 1356 | 1357 | func Test_EmptySetProperties(t *testing.T) { 1358 | empty := NewSet[string]() 1359 | 1360 | a := NewSet[string]() 1361 | a.Add("1") 1362 | a.Add("foo") 1363 | a.Add("bar") 1364 | 1365 | b := NewSet[string]() 1366 | b.Add("one") 1367 | b.Add("two") 1368 | b.Add("3") 1369 | b.Add("4") 1370 | 1371 | if !empty.IsSubset(a) || !empty.IsSubset(b) { 1372 | t.Error("The empty set is supposed to be a subset of all sets") 1373 | } 1374 | 1375 | if !a.IsSuperset(empty) || !b.IsSuperset(empty) { 1376 | t.Error("All sets are supposed to be a superset of the empty set") 1377 | } 1378 | 1379 | if !empty.IsSubset(empty) || !empty.IsSuperset(empty) { 1380 | t.Error("The empty set is supposed to be a subset and a superset of itself") 1381 | } 1382 | 1383 | c := a.Union(empty) 1384 | if !c.Equal(a) { 1385 | t.Error("The union of any set with the empty set is supposed to be equal to itself") 1386 | } 1387 | 1388 | c = a.Intersect(empty) 1389 | if !c.Equal(empty) { 1390 | t.Error("The intersection of any set with the empty set is supposed to be the empty set") 1391 | } 1392 | 1393 | if empty.Cardinality() != 0 { 1394 | t.Error("Cardinality of the empty set is supposed to be zero") 1395 | } 1396 | } 1397 | 1398 | func Test_ToSliceUnthreadsafe(t *testing.T) { 1399 | s := makeUnsafeSetInt([]int{1, 2, 3}) 1400 | setAsSlice := s.ToSlice() 1401 | if len(setAsSlice) != s.Cardinality() { 1402 | t.Errorf("Set length is incorrect: %v", len(setAsSlice)) 1403 | } 1404 | 1405 | for _, i := range setAsSlice { 1406 | if !s.Contains(i) { 1407 | t.Errorf("Set is missing element: %v", i) 1408 | } 1409 | } 1410 | } 1411 | 1412 | func Test_NewSetFromMapKey_Ints(t *testing.T) { 1413 | m := map[int]int{ 1414 | 5: 5, 1415 | 2: 3, 1416 | } 1417 | 1418 | s := NewSetFromMapKeys(m) 1419 | 1420 | if len(m) != s.Cardinality() { 1421 | t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality()) 1422 | } 1423 | 1424 | for k := range m { 1425 | if !s.Contains(k) { 1426 | t.Errorf("Element %d not found in map: %v", k, m) 1427 | } 1428 | } 1429 | } 1430 | 1431 | func Test_NewSetFromMapKey_Strings(t *testing.T) { 1432 | m := map[int]int{ 1433 | 5: 5, 1434 | 2: 3, 1435 | } 1436 | 1437 | s := NewSetFromMapKeys(m) 1438 | 1439 | if len(m) != s.Cardinality() { 1440 | t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality()) 1441 | } 1442 | 1443 | for k := range m { 1444 | if !s.Contains(k) { 1445 | t.Errorf("Element %q not found in map: %v", k, m) 1446 | } 1447 | } 1448 | } 1449 | 1450 | func Test_NewThreadUnsafeSetFromMapKey_Ints(t *testing.T) { 1451 | m := map[int]int{ 1452 | 5: 5, 1453 | 2: 3, 1454 | } 1455 | 1456 | s := NewThreadUnsafeSetFromMapKeys(m) 1457 | 1458 | if len(m) != s.Cardinality() { 1459 | t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality()) 1460 | } 1461 | 1462 | for k := range m { 1463 | if !s.Contains(k) { 1464 | t.Errorf("Element %d not found in map: %v", k, m) 1465 | } 1466 | } 1467 | } 1468 | 1469 | func Test_NewThreadUnsafeSetFromMapKey_Strings(t *testing.T) { 1470 | m := map[int]int{ 1471 | 5: 5, 1472 | 2: 3, 1473 | } 1474 | 1475 | s := NewThreadUnsafeSetFromMapKeys(m) 1476 | 1477 | if len(m) != s.Cardinality() { 1478 | t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality()) 1479 | } 1480 | 1481 | for k := range m { 1482 | if !s.Contains(k) { 1483 | t.Errorf("Element %q not found in map: %v", k, m) 1484 | } 1485 | } 1486 | } 1487 | 1488 | func Test_Elements(t *testing.T) { 1489 | a := NewSet[string]() 1490 | 1491 | a.Add("Z") 1492 | a.Add("Y") 1493 | a.Add("X") 1494 | a.Add("W") 1495 | 1496 | b := NewSet[string]() 1497 | Elements(a)(func(elem string) bool { 1498 | b.Add(elem) 1499 | return true 1500 | }) 1501 | 1502 | if !a.Equal(b) { 1503 | t.Error("The sets are not equal after iterating (Each) through the first set") 1504 | } 1505 | 1506 | var count int 1507 | Elements(a)(func(elem string) bool { 1508 | if count == 2 { 1509 | return false 1510 | } 1511 | count++ 1512 | return true 1513 | }) 1514 | 1515 | if count != 2 { 1516 | t.Error("Iteration should stop on the way") 1517 | } 1518 | } 1519 | 1520 | func Test_Example(t *testing.T) { 1521 | /* 1522 | requiredClasses := NewSet() 1523 | requiredClasses.Add("Cooking") 1524 | requiredClasses.Add("English") 1525 | requiredClasses.Add("Math") 1526 | requiredClasses.Add("Biology") 1527 | 1528 | scienceSlice := []interface{}{"Biology", "Chemistry"} 1529 | scienceClasses := NewSet(scienceSlice) 1530 | 1531 | electiveClasses := NewSet() 1532 | electiveClasses.Add("Welding") 1533 | electiveClasses.Add("Music") 1534 | electiveClasses.Add("Automotive") 1535 | 1536 | bonusClasses := NewSet() 1537 | bonusClasses.Add("Go Programming") 1538 | bonusClasses.Add("Python Programming") 1539 | 1540 | //Show me all the available classes I can take 1541 | allClasses := requiredClasses.Union(scienceClasses).Union(electiveClasses).Union(bonusClasses) 1542 | fmt.Println(allClasses) //Set{English, Chemistry, Automotive, Cooking, Math, Biology, Welding, Music, Go Programming} 1543 | 1544 | //Is cooking considered a science class? 1545 | fmt.Println(scienceClasses.Contains("Cooking")) //false 1546 | 1547 | //Show me all classes that are not science classes, since I hate science. 1548 | fmt.Println(allClasses.Difference(scienceClasses)) //Set{English, Automotive, Cooking, Math, Welding, Music, Go Programming} 1549 | 1550 | //Which science classes are also required classes? 1551 | fmt.Println(scienceClasses.Intersect(requiredClasses)) //Set{Biology} 1552 | 1553 | //How many bonus classes do you offer? 1554 | fmt.Println(bonusClasses.Cardinality()) //2 1555 | 1556 | //Do you have the following classes? Welding, Automotive and English? 1557 | fmt.Println(allClasses.ContainsAll("Welding", "Automotive", "English")) 1558 | */ 1559 | } 1560 | -------------------------------------------------------------------------------- /sorted.go: -------------------------------------------------------------------------------- 1 | //go:build go1.21 2 | // +build go1.21 3 | 4 | /* 5 | Open Source Initiative OSI - The MIT License (MIT):Licensing 6 | 7 | The MIT License (MIT) 8 | Copyright (c) 2013 - 2023 Ralph Caraveo (deckarep@gmail.com) 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of 11 | this software and associated documentation files (the "Software"), to deal in 12 | the Software without restriction, including without limitation the rights to 13 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 14 | of the Software, and to permit persons to whom the Software is furnished to do 15 | so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | package mapset 30 | 31 | import ( 32 | "cmp" 33 | "slices" 34 | ) 35 | 36 | // Sorted returns a sorted slice of a set of any ordered type in ascending order. 37 | // When sorting floating-point numbers, NaNs are ordered before other values. 38 | func Sorted[E cmp.Ordered](set Set[E]) []E { 39 | s := set.ToSlice() 40 | slices.Sort(s) 41 | return s 42 | } 43 | -------------------------------------------------------------------------------- /sorted_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.21 2 | // +build go1.21 3 | 4 | /* 5 | Open Source Initiative OSI - The MIT License (MIT):Licensing 6 | 7 | The MIT License (MIT) 8 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of 11 | this software and associated documentation files (the "Software"), to deal in 12 | the Software without restriction, including without limitation the rights to 13 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 14 | of the Software, and to permit persons to whom the Software is furnished to do 15 | so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | package mapset 30 | 31 | import ( 32 | "testing" 33 | ) 34 | 35 | func Test_Sorted(t *testing.T) { 36 | test := func(t *testing.T, ctor func(vals ...string) Set[string]) { 37 | set := ctor("apple", "banana", "pear") 38 | sorted := Sorted(set) 39 | 40 | if len(sorted) != set.Cardinality() { 41 | t.Errorf("Length of slice is not the same as the set. Expected: %d. Actual: %d", set.Cardinality(), len(sorted)) 42 | } 43 | 44 | if sorted[0] != "apple" { 45 | t.Errorf("Element 0 was not equal to apple: %s", sorted[0]) 46 | } 47 | if sorted[1] != "banana" { 48 | t.Errorf("Element 1 was not equal to banana: %s", sorted[1]) 49 | } 50 | if sorted[2] != "pear" { 51 | t.Errorf("Element 2 was not equal to pear: %s", sorted[2]) 52 | } 53 | } 54 | 55 | t.Run("Safe", func(t *testing.T) { 56 | test(t, NewSet[string]) 57 | }) 58 | t.Run("Unsafe", func(t *testing.T) { 59 | test(t, NewThreadUnsafeSet[string]) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /threadsafe.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "sync" 30 | 31 | "go.mongodb.org/mongo-driver/bson/bsontype" 32 | ) 33 | 34 | type threadSafeSet[T comparable] struct { 35 | sync.RWMutex 36 | uss *threadUnsafeSet[T] 37 | } 38 | 39 | func newThreadSafeSet[T comparable]() *threadSafeSet[T] { 40 | return &threadSafeSet[T]{ 41 | uss: newThreadUnsafeSet[T](), 42 | } 43 | } 44 | 45 | func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] { 46 | return &threadSafeSet[T]{ 47 | uss: newThreadUnsafeSetWithSize[T](cardinality), 48 | } 49 | } 50 | 51 | func (t *threadSafeSet[T]) Add(v T) bool { 52 | t.Lock() 53 | ret := t.uss.Add(v) 54 | t.Unlock() 55 | return ret 56 | } 57 | 58 | func (t *threadSafeSet[T]) Append(v ...T) int { 59 | t.Lock() 60 | ret := t.uss.Append(v...) 61 | t.Unlock() 62 | return ret 63 | } 64 | 65 | func (t *threadSafeSet[T]) Contains(v ...T) bool { 66 | t.RLock() 67 | ret := t.uss.Contains(v...) 68 | t.RUnlock() 69 | 70 | return ret 71 | } 72 | 73 | func (t *threadSafeSet[T]) ContainsOne(v T) bool { 74 | t.RLock() 75 | ret := t.uss.ContainsOne(v) 76 | t.RUnlock() 77 | 78 | return ret 79 | } 80 | 81 | func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { 82 | t.RLock() 83 | ret := t.uss.ContainsAny(v...) 84 | t.RUnlock() 85 | 86 | return ret 87 | } 88 | 89 | func (t *threadSafeSet[T]) ContainsAnyElement(other Set[T]) bool { 90 | o := other.(*threadSafeSet[T]) 91 | 92 | t.RLock() 93 | o.RLock() 94 | 95 | ret := t.uss.ContainsAnyElement(o.uss) 96 | 97 | t.RUnlock() 98 | o.RUnlock() 99 | return ret 100 | } 101 | 102 | func (t *threadSafeSet[T]) IsEmpty() bool { 103 | return t.Cardinality() == 0 104 | } 105 | 106 | func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { 107 | o := other.(*threadSafeSet[T]) 108 | 109 | t.RLock() 110 | o.RLock() 111 | 112 | ret := t.uss.IsSubset(o.uss) 113 | t.RUnlock() 114 | o.RUnlock() 115 | return ret 116 | } 117 | 118 | func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool { 119 | o := other.(*threadSafeSet[T]) 120 | 121 | t.RLock() 122 | defer t.RUnlock() 123 | o.RLock() 124 | defer o.RUnlock() 125 | 126 | return t.uss.IsProperSubset(o.uss) 127 | } 128 | 129 | func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool { 130 | return other.IsSubset(t) 131 | } 132 | 133 | func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool { 134 | return other.IsProperSubset(t) 135 | } 136 | 137 | func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { 138 | o := other.(*threadSafeSet[T]) 139 | 140 | t.RLock() 141 | o.RLock() 142 | 143 | unsafeUnion := t.uss.Union(o.uss).(*threadUnsafeSet[T]) 144 | ret := &threadSafeSet[T]{uss: unsafeUnion} 145 | t.RUnlock() 146 | o.RUnlock() 147 | return ret 148 | } 149 | 150 | func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { 151 | o := other.(*threadSafeSet[T]) 152 | 153 | t.RLock() 154 | o.RLock() 155 | 156 | unsafeIntersection := t.uss.Intersect(o.uss).(*threadUnsafeSet[T]) 157 | ret := &threadSafeSet[T]{uss: unsafeIntersection} 158 | t.RUnlock() 159 | o.RUnlock() 160 | return ret 161 | } 162 | 163 | func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { 164 | o := other.(*threadSafeSet[T]) 165 | 166 | t.RLock() 167 | o.RLock() 168 | 169 | unsafeDifference := t.uss.Difference(o.uss).(*threadUnsafeSet[T]) 170 | ret := &threadSafeSet[T]{uss: unsafeDifference} 171 | t.RUnlock() 172 | o.RUnlock() 173 | return ret 174 | } 175 | 176 | func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { 177 | o := other.(*threadSafeSet[T]) 178 | 179 | t.RLock() 180 | o.RLock() 181 | 182 | unsafeDifference := t.uss.SymmetricDifference(o.uss).(*threadUnsafeSet[T]) 183 | ret := &threadSafeSet[T]{uss: unsafeDifference} 184 | t.RUnlock() 185 | o.RUnlock() 186 | return ret 187 | } 188 | 189 | func (t *threadSafeSet[T]) Clear() { 190 | t.Lock() 191 | t.uss.Clear() 192 | t.Unlock() 193 | } 194 | 195 | func (t *threadSafeSet[T]) Remove(v T) { 196 | t.Lock() 197 | delete(*t.uss, v) 198 | t.Unlock() 199 | } 200 | 201 | func (t *threadSafeSet[T]) RemoveAll(i ...T) { 202 | t.Lock() 203 | t.uss.RemoveAll(i...) 204 | t.Unlock() 205 | } 206 | 207 | func (t *threadSafeSet[T]) Cardinality() int { 208 | t.RLock() 209 | defer t.RUnlock() 210 | return len(*t.uss) 211 | } 212 | 213 | func (t *threadSafeSet[T]) Each(cb func(T) bool) { 214 | t.RLock() 215 | defer t.RUnlock() 216 | for elem := range *t.uss { 217 | if cb(elem) { 218 | break 219 | } 220 | } 221 | } 222 | 223 | func (t *threadSafeSet[T]) Iter() <-chan T { 224 | ch := make(chan T) 225 | go func() { 226 | t.RLock() 227 | 228 | for elem := range *t.uss { 229 | ch <- elem 230 | } 231 | close(ch) 232 | t.RUnlock() 233 | }() 234 | 235 | return ch 236 | } 237 | 238 | func (t *threadSafeSet[T]) Iterator() *Iterator[T] { 239 | iterator, ch, stopCh := newIterator[T]() 240 | 241 | go func() { 242 | t.RLock() 243 | L: 244 | for elem := range *t.uss { 245 | select { 246 | case <-stopCh: 247 | break L 248 | case ch <- elem: 249 | } 250 | } 251 | close(ch) 252 | t.RUnlock() 253 | }() 254 | 255 | return iterator 256 | } 257 | 258 | func (t *threadSafeSet[T]) Equal(other Set[T]) bool { 259 | o := other.(*threadSafeSet[T]) 260 | 261 | t.RLock() 262 | o.RLock() 263 | 264 | ret := t.uss.Equal(o.uss) 265 | t.RUnlock() 266 | o.RUnlock() 267 | return ret 268 | } 269 | 270 | func (t *threadSafeSet[T]) Clone() Set[T] { 271 | t.RLock() 272 | 273 | unsafeClone := t.uss.Clone().(*threadUnsafeSet[T]) 274 | ret := &threadSafeSet[T]{uss: unsafeClone} 275 | t.RUnlock() 276 | return ret 277 | } 278 | 279 | func (t *threadSafeSet[T]) String() string { 280 | t.RLock() 281 | ret := t.uss.String() 282 | t.RUnlock() 283 | return ret 284 | } 285 | 286 | func (t *threadSafeSet[T]) Pop() (T, bool) { 287 | t.Lock() 288 | defer t.Unlock() 289 | return t.uss.Pop() 290 | } 291 | 292 | func (t *threadSafeSet[T]) PopN(n int) ([]T, int) { 293 | t.Lock() 294 | defer t.Unlock() 295 | return t.uss.PopN(n) 296 | } 297 | 298 | func (t *threadSafeSet[T]) ToSlice() []T { 299 | t.RLock() 300 | l := len(*t.uss) 301 | keys := make([]T, 0, l) 302 | for elem := range *t.uss { 303 | keys = append(keys, elem) 304 | } 305 | t.RUnlock() 306 | return keys 307 | } 308 | 309 | func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { 310 | t.RLock() 311 | b, err := t.uss.MarshalJSON() 312 | t.RUnlock() 313 | 314 | return b, err 315 | } 316 | 317 | func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { 318 | t.Lock() 319 | err := t.uss.UnmarshalJSON(p) 320 | t.Unlock() 321 | 322 | return err 323 | } 324 | 325 | func (t *threadSafeSet[T]) MarshalBSONValue() (bsontype.Type, []byte, error) { 326 | t.RLock() 327 | bt, b, err := t.uss.MarshalBSONValue() 328 | t.RUnlock() 329 | 330 | return bt, b, err 331 | } 332 | 333 | func (t *threadSafeSet[T]) UnmarshalBSONValue(bt bsontype.Type, p []byte) error { 334 | t.Lock() 335 | err := t.uss.UnmarshalBSONValue(bt, p) 336 | t.Unlock() 337 | 338 | return err 339 | } 340 | -------------------------------------------------------------------------------- /threadsafe_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "encoding/json" 30 | "fmt" 31 | "math/rand" 32 | "runtime" 33 | "sync" 34 | "sync/atomic" 35 | "testing" 36 | 37 | "go.mongodb.org/mongo-driver/bson" 38 | ) 39 | 40 | const N = 1000 41 | 42 | func Test_AddConcurrent(t *testing.T) { 43 | runtime.GOMAXPROCS(2) 44 | 45 | s := NewSet[int]() 46 | ints := rand.Perm(N) 47 | 48 | var wg sync.WaitGroup 49 | wg.Add(len(ints)) 50 | for i := 0; i < len(ints); i++ { 51 | go func(i int) { 52 | s.Add(i) 53 | wg.Done() 54 | }(i) 55 | } 56 | 57 | wg.Wait() 58 | for _, i := range ints { 59 | if !s.Contains(i) { 60 | t.Errorf("Set is missing element: %v", i) 61 | } 62 | } 63 | } 64 | 65 | func Test_AppendConcurrent(t *testing.T) { 66 | runtime.GOMAXPROCS(2) 67 | 68 | s := NewSet[int]() 69 | ints := rand.Perm(N) 70 | 71 | n := len(ints) >> 1 72 | var wg sync.WaitGroup 73 | wg.Add(n) 74 | for i := 0; i < n; i++ { 75 | go func(i int) { 76 | s.Append(i, N-i-1) 77 | wg.Done() 78 | }(i) 79 | } 80 | 81 | wg.Wait() 82 | for _, i := range ints { 83 | if !s.Contains(i) { 84 | t.Errorf("Set is missing element: %v", i) 85 | } 86 | } 87 | } 88 | 89 | func Test_CardinalityConcurrent(t *testing.T) { 90 | runtime.GOMAXPROCS(2) 91 | 92 | s := NewSet[int]() 93 | 94 | var wg sync.WaitGroup 95 | wg.Add(1) 96 | go func() { 97 | elems := s.Cardinality() 98 | for i := 0; i < N; i++ { 99 | newElems := s.Cardinality() 100 | if newElems < elems { 101 | t.Errorf("Cardinality shrunk from %v to %v", elems, newElems) 102 | } 103 | } 104 | wg.Done() 105 | }() 106 | 107 | for i := 0; i < N; i++ { 108 | s.Add(rand.Int()) 109 | } 110 | wg.Wait() 111 | } 112 | 113 | func Test_ClearConcurrent(t *testing.T) { 114 | runtime.GOMAXPROCS(2) 115 | 116 | s := NewSet[int]() 117 | ints := rand.Perm(N) 118 | 119 | var wg sync.WaitGroup 120 | wg.Add(len(ints)) 121 | for i := 0; i < len(ints); i++ { 122 | go func() { 123 | s.Clear() 124 | wg.Done() 125 | }() 126 | go func(i int) { 127 | s.Add(i) 128 | }(i) 129 | } 130 | 131 | wg.Wait() 132 | } 133 | 134 | func Test_CloneConcurrent(t *testing.T) { 135 | runtime.GOMAXPROCS(2) 136 | 137 | s := NewSet[int]() 138 | ints := rand.Perm(N) 139 | 140 | for _, v := range ints { 141 | s.Add(v) 142 | } 143 | 144 | var wg sync.WaitGroup 145 | wg.Add(len(ints)) 146 | for i := range ints { 147 | go func(i int) { 148 | s.Remove(i) 149 | wg.Done() 150 | }(i) 151 | } 152 | s.Clone() 153 | wg.Wait() 154 | } 155 | 156 | func Test_ContainsConcurrent(t *testing.T) { 157 | runtime.GOMAXPROCS(2) 158 | 159 | s := NewSet[int]() 160 | ints := rand.Perm(N) 161 | integers := make([]int, 0) 162 | for _, v := range ints { 163 | s.Add(v) 164 | integers = append(integers, v) 165 | } 166 | 167 | var wg sync.WaitGroup 168 | for range ints { 169 | wg.Add(1) 170 | go func() { 171 | s.Contains(integers...) 172 | wg.Done() 173 | }() 174 | } 175 | wg.Wait() 176 | } 177 | 178 | func Test_ContainsOneConcurrent(t *testing.T) { 179 | runtime.GOMAXPROCS(2) 180 | 181 | s := NewSet[int]() 182 | ints := rand.Perm(N) 183 | for _, v := range ints { 184 | s.Add(v) 185 | } 186 | 187 | var wg sync.WaitGroup 188 | for _, v := range ints { 189 | number := v 190 | wg.Add(1) 191 | go func() { 192 | s.ContainsOne(number) 193 | wg.Done() 194 | }() 195 | } 196 | wg.Wait() 197 | } 198 | 199 | func Test_ContainsAnyConcurrent(t *testing.T) { 200 | runtime.GOMAXPROCS(2) 201 | 202 | s := NewSet[int]() 203 | ints := rand.Perm(N) 204 | integers := make([]int, 0) 205 | for _, v := range ints { 206 | if v%N == 0 { 207 | s.Add(v) 208 | } 209 | integers = append(integers, v) 210 | } 211 | 212 | var wg sync.WaitGroup 213 | for range ints { 214 | wg.Add(1) 215 | go func() { 216 | s.ContainsAny(integers...) 217 | wg.Done() 218 | }() 219 | } 220 | wg.Wait() 221 | } 222 | 223 | func Test_ContainsAnyElementConcurrent(t *testing.T) { 224 | runtime.GOMAXPROCS(2) 225 | 226 | s, ss := NewSet[int](), NewSet[int]() 227 | ints := rand.Perm(N) 228 | for _, v := range ints { 229 | s.Add(v) 230 | ss.Add(v) 231 | } 232 | 233 | var wg sync.WaitGroup 234 | for range ints { 235 | wg.Add(1) 236 | go func() { 237 | s.ContainsAnyElement(ss) 238 | wg.Done() 239 | }() 240 | } 241 | wg.Wait() 242 | } 243 | 244 | func Test_DifferenceConcurrent(t *testing.T) { 245 | runtime.GOMAXPROCS(2) 246 | 247 | s, ss := NewSet[int](), NewSet[int]() 248 | ints := rand.Perm(N) 249 | for _, v := range ints { 250 | s.Add(v) 251 | ss.Add(v) 252 | } 253 | 254 | var wg sync.WaitGroup 255 | for range ints { 256 | wg.Add(1) 257 | go func() { 258 | s.Difference(ss) 259 | wg.Done() 260 | }() 261 | } 262 | wg.Wait() 263 | } 264 | 265 | func Test_EqualConcurrent(t *testing.T) { 266 | runtime.GOMAXPROCS(2) 267 | 268 | s, ss := NewSet[int](), NewSet[int]() 269 | ints := rand.Perm(N) 270 | for _, v := range ints { 271 | s.Add(v) 272 | ss.Add(v) 273 | } 274 | 275 | var wg sync.WaitGroup 276 | for range ints { 277 | wg.Add(1) 278 | go func() { 279 | s.Equal(ss) 280 | wg.Done() 281 | }() 282 | } 283 | wg.Wait() 284 | } 285 | 286 | func Test_IntersectConcurrent(t *testing.T) { 287 | runtime.GOMAXPROCS(2) 288 | 289 | s, ss := NewSet[int](), NewSet[int]() 290 | ints := rand.Perm(N) 291 | for _, v := range ints { 292 | s.Add(v) 293 | ss.Add(v) 294 | } 295 | 296 | var wg sync.WaitGroup 297 | for range ints { 298 | wg.Add(1) 299 | go func() { 300 | s.Intersect(ss) 301 | wg.Done() 302 | }() 303 | } 304 | wg.Wait() 305 | } 306 | 307 | func Test_IsEmptyConcurrent(t *testing.T) { 308 | runtime.GOMAXPROCS(2) 309 | 310 | s := NewSet[int]() 311 | 312 | var wg sync.WaitGroup 313 | wg.Add(1) 314 | go func() { 315 | for i := 0; i < N; i++ { 316 | size := s.Cardinality() 317 | if s.IsEmpty() && size > 0 { 318 | t.Errorf("Is Empty should be return false") 319 | } 320 | } 321 | wg.Done() 322 | }() 323 | 324 | for i := 0; i < N; i++ { 325 | s.Add(rand.Int()) 326 | } 327 | wg.Wait() 328 | } 329 | 330 | func Test_IsSubsetConcurrent(t *testing.T) { 331 | runtime.GOMAXPROCS(2) 332 | 333 | s, ss := NewSet[int](), NewSet[int]() 334 | ints := rand.Perm(N) 335 | for _, v := range ints { 336 | s.Add(v) 337 | ss.Add(v) 338 | } 339 | 340 | var wg sync.WaitGroup 341 | for range ints { 342 | wg.Add(1) 343 | go func() { 344 | s.IsSubset(ss) 345 | wg.Done() 346 | }() 347 | } 348 | wg.Wait() 349 | } 350 | 351 | func Test_IsProperSubsetConcurrent(t *testing.T) { 352 | runtime.GOMAXPROCS(2) 353 | 354 | s, ss := NewSet[int](), NewSet[int]() 355 | ints := rand.Perm(N) 356 | for _, v := range ints { 357 | s.Add(v) 358 | ss.Add(v) 359 | } 360 | 361 | var wg sync.WaitGroup 362 | for range ints { 363 | wg.Add(1) 364 | go func() { 365 | s.IsProperSubset(ss) 366 | wg.Done() 367 | }() 368 | } 369 | wg.Wait() 370 | } 371 | 372 | func Test_IsSupersetConcurrent(t *testing.T) { 373 | runtime.GOMAXPROCS(2) 374 | 375 | s, ss := NewSet[int](), NewSet[int]() 376 | ints := rand.Perm(N) 377 | for _, v := range ints { 378 | s.Add(v) 379 | ss.Add(v) 380 | } 381 | 382 | var wg sync.WaitGroup 383 | for range ints { 384 | wg.Add(1) 385 | go func() { 386 | s.IsSuperset(ss) 387 | wg.Done() 388 | }() 389 | } 390 | wg.Wait() 391 | } 392 | 393 | func Test_IsProperSupersetConcurrent(t *testing.T) { 394 | runtime.GOMAXPROCS(2) 395 | 396 | s, ss := NewSet[int](), NewSet[int]() 397 | ints := rand.Perm(N) 398 | for _, v := range ints { 399 | s.Add(v) 400 | ss.Add(v) 401 | } 402 | 403 | var wg sync.WaitGroup 404 | for range ints { 405 | wg.Add(1) 406 | go func() { 407 | s.IsProperSuperset(ss) 408 | wg.Done() 409 | }() 410 | } 411 | wg.Wait() 412 | } 413 | 414 | func Test_EachConcurrent(t *testing.T) { 415 | runtime.GOMAXPROCS(2) 416 | concurrent := 10 417 | 418 | s := NewSet[int]() 419 | ints := rand.Perm(N) 420 | for _, v := range ints { 421 | s.Add(v) 422 | } 423 | 424 | var count int64 425 | wg := new(sync.WaitGroup) 426 | wg.Add(concurrent) 427 | for n := 0; n < concurrent; n++ { 428 | go func() { 429 | defer wg.Done() 430 | s.Each(func(elem int) bool { 431 | atomic.AddInt64(&count, 1) 432 | return false 433 | }) 434 | }() 435 | } 436 | wg.Wait() 437 | 438 | if count != int64(N*concurrent) { 439 | t.Errorf("%v != %v", count, int64(N*concurrent)) 440 | } 441 | } 442 | 443 | func Test_IterConcurrent(t *testing.T) { 444 | runtime.GOMAXPROCS(2) 445 | 446 | s := NewSet[int]() 447 | ints := rand.Perm(N) 448 | for _, v := range ints { 449 | s.Add(v) 450 | } 451 | 452 | cs := make([]<-chan int, 0) 453 | for range ints { 454 | cs = append(cs, s.Iter()) 455 | } 456 | 457 | c := make(chan interface{}) 458 | go func() { 459 | for n := 0; n < len(ints)*N; { 460 | for _, d := range cs { 461 | select { 462 | case <-d: 463 | n++ 464 | c <- nil 465 | default: 466 | } 467 | } 468 | } 469 | close(c) 470 | }() 471 | 472 | for range c { 473 | } 474 | } 475 | 476 | func Test_RemoveConcurrent(t *testing.T) { 477 | runtime.GOMAXPROCS(2) 478 | 479 | s := NewSet[int]() 480 | ints := rand.Perm(N) 481 | for _, v := range ints { 482 | s.Add(v) 483 | } 484 | 485 | var wg sync.WaitGroup 486 | wg.Add(len(ints)) 487 | for _, v := range ints { 488 | go func(i int) { 489 | s.Remove(i) 490 | wg.Done() 491 | }(v) 492 | } 493 | wg.Wait() 494 | 495 | if s.Cardinality() != 0 { 496 | t.Errorf("Expected cardinality 0; got %v", s.Cardinality()) 497 | } 498 | } 499 | 500 | func Test_StringConcurrent(t *testing.T) { 501 | runtime.GOMAXPROCS(2) 502 | 503 | s := NewSet[int]() 504 | ints := rand.Perm(N) 505 | for _, v := range ints { 506 | s.Add(v) 507 | } 508 | 509 | var wg sync.WaitGroup 510 | wg.Add(len(ints)) 511 | for range ints { 512 | go func() { 513 | _ = s.String() 514 | wg.Done() 515 | }() 516 | } 517 | wg.Wait() 518 | } 519 | 520 | func Test_SymmetricDifferenceConcurrent(t *testing.T) { 521 | runtime.GOMAXPROCS(2) 522 | 523 | s, ss := NewSet[int](), NewSet[int]() 524 | ints := rand.Perm(N) 525 | for _, v := range ints { 526 | s.Add(v) 527 | ss.Add(v) 528 | } 529 | 530 | var wg sync.WaitGroup 531 | for range ints { 532 | wg.Add(1) 533 | go func() { 534 | s.SymmetricDifference(ss) 535 | wg.Done() 536 | }() 537 | } 538 | wg.Wait() 539 | } 540 | 541 | func Test_ToSlice(t *testing.T) { 542 | runtime.GOMAXPROCS(2) 543 | 544 | s := NewSet[int]() 545 | ints := rand.Perm(N) 546 | 547 | var wg sync.WaitGroup 548 | wg.Add(len(ints)) 549 | for i := 0; i < len(ints); i++ { 550 | go func(i int) { 551 | s.Add(i) 552 | wg.Done() 553 | }(i) 554 | } 555 | 556 | wg.Wait() 557 | setAsSlice := s.ToSlice() 558 | if len(setAsSlice) != s.Cardinality() { 559 | t.Errorf("Set length is incorrect: %v", len(setAsSlice)) 560 | } 561 | 562 | for _, i := range setAsSlice { 563 | if !s.Contains(i) { 564 | t.Errorf("Set is missing element: %v", i) 565 | } 566 | } 567 | } 568 | 569 | // Test_ToSliceDeadlock - fixes issue: https://github.com/deckarep/golang-set/issues/36 570 | // This code reveals the deadlock however it doesn't happen consistently. 571 | func Test_ToSliceDeadlock(t *testing.T) { 572 | runtime.GOMAXPROCS(2) 573 | 574 | var wg sync.WaitGroup 575 | set := NewSet[int]() 576 | workers := 10 577 | wg.Add(workers) 578 | for i := 1; i <= workers; i++ { 579 | go func() { 580 | for j := 0; j < 1000; j++ { 581 | set.Add(1) 582 | set.ToSlice() 583 | } 584 | wg.Done() 585 | }() 586 | } 587 | wg.Wait() 588 | } 589 | 590 | func Test_UnmarshalJSON(t *testing.T) { 591 | s := []byte(`["test", "1", "2", "3"]`) //,["4,5,6"]]`) 592 | expected := NewSet( 593 | []string{ 594 | string(json.Number("1")), 595 | string(json.Number("2")), 596 | string(json.Number("3")), 597 | "test", 598 | }..., 599 | ) 600 | 601 | actual := NewSet[string]() 602 | err := json.Unmarshal(s, actual) 603 | if err != nil { 604 | t.Errorf("Error should be nil: %v", err) 605 | } 606 | 607 | if !expected.Equal(actual) { 608 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 609 | } 610 | } 611 | 612 | func Test_MarshalJSON(t *testing.T) { 613 | expected := NewSet( 614 | []string{ 615 | string(json.Number("1")), 616 | "test", 617 | }..., 618 | ) 619 | 620 | b, err := json.Marshal( 621 | NewSet( 622 | []string{ 623 | "1", 624 | "test", 625 | }..., 626 | ), 627 | ) 628 | if err != nil { 629 | t.Errorf("Error should be nil: %v", err) 630 | } 631 | 632 | actual := NewSet[string]() 633 | err = json.Unmarshal(b, actual) 634 | if err != nil { 635 | t.Errorf("Error should be nil: %v", err) 636 | } 637 | 638 | if !expected.Equal(actual) { 639 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 640 | } 641 | } 642 | 643 | // Test_DeadlockOnEachCallbackWhenPanic ensures that should a panic occur within the context 644 | // of the Each callback, progress can still be made on recovery. This is an edge case 645 | // that was called out on issue: https://github.com/deckarep/golang-set/issues/163. 646 | func Test_DeadlockOnEachCallbackWhenPanic(t *testing.T) { 647 | numbers := []int{1, 2, 3, 4} 648 | widgets := NewSet[*int]() 649 | widgets.Append(&numbers[0], &numbers[1], nil, &numbers[2]) 650 | 651 | var panicOccured = false 652 | 653 | doWork := func(s Set[*int]) (err error) { 654 | defer func() { 655 | if r := recover(); r != nil { 656 | panicOccured = true 657 | err = fmt.Errorf("failed to do work: %v", r) 658 | } 659 | }() 660 | 661 | s.Each(func(n *int) bool { 662 | // NOTE: this will throw a panic once we get to the nil element. 663 | _ = *n * 2 664 | return false 665 | }) 666 | 667 | return nil 668 | } 669 | 670 | card := widgets.Cardinality() 671 | if widgets.Cardinality() != 4 { 672 | t.Errorf("Expected widgets to have 4 elements, but has %d", card) 673 | } 674 | 675 | doWork(widgets) 676 | 677 | if !panicOccured { 678 | t.Error("Expected a panic to occur followed by recover for test to be valid") 679 | } 680 | 681 | widgets.Add(&numbers[3]) 682 | 683 | card = widgets.Cardinality() 684 | if widgets.Cardinality() != 5 { 685 | t.Errorf("Expected widgets to have 5 elements, but has %d", card) 686 | } 687 | } 688 | 689 | func Test_UnmarshalBSONValue(t *testing.T) { 690 | tp, s, initErr := bson.MarshalValue( 691 | bson.A{"1", "2", "3", "test"}, 692 | ) 693 | 694 | if initErr != nil { 695 | t.Errorf("Init Error should be nil: %v", initErr) 696 | 697 | return 698 | } 699 | 700 | if tp != bson.TypeArray { 701 | t.Errorf("Encoded Type should be bson.Array, got: %v", tp) 702 | 703 | return 704 | } 705 | 706 | expected := NewSet("1", "2", "3", "test") 707 | actual := NewSet[string]() 708 | err := bson.UnmarshalValue(bson.TypeArray, s, actual) 709 | if err != nil { 710 | t.Errorf("Error should be nil: %v", err) 711 | } 712 | 713 | if !expected.Equal(actual) { 714 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 715 | } 716 | } 717 | 718 | func TestThreadUnsafeSet_UnmarshalBSONValue(t *testing.T) { 719 | tp, s, initErr := bson.MarshalValue( 720 | bson.A{int64(1), int64(2), int64(3)}, 721 | ) 722 | 723 | if initErr != nil { 724 | t.Errorf("Init Error should be nil: %v", initErr) 725 | 726 | return 727 | } 728 | 729 | if tp != bson.TypeArray { 730 | t.Errorf("Encoded Type should be bson.Array, got: %v", tp) 731 | 732 | return 733 | } 734 | 735 | expected := NewThreadUnsafeSet[int64](1, 2, 3) 736 | actual := NewThreadUnsafeSet[int64]() 737 | err := actual.UnmarshalBSONValue(bson.TypeArray, []byte(s)) 738 | if err != nil { 739 | t.Errorf("Error should be nil: %v", err) 740 | } 741 | if !expected.Equal(actual) { 742 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 743 | } 744 | } 745 | 746 | func Test_MarshalBSONValue(t *testing.T) { 747 | expected := NewSet("1", "test") 748 | 749 | _, b, err := bson.MarshalValue( 750 | NewSet("1", "test"), 751 | ) 752 | if err != nil { 753 | t.Errorf("Error should be nil: %v", err) 754 | } 755 | 756 | actual := NewSet[string]() 757 | err = bson.UnmarshalValue(bson.TypeArray, b, actual) 758 | if err != nil { 759 | t.Errorf("Error should be nil: %v", err) 760 | } 761 | 762 | if !expected.Equal(actual) { 763 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 764 | } 765 | } 766 | -------------------------------------------------------------------------------- /threadunsafe.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "encoding/json" 30 | "fmt" 31 | "strings" 32 | 33 | "go.mongodb.org/mongo-driver/bson" 34 | "go.mongodb.org/mongo-driver/bson/bsontype" 35 | ) 36 | 37 | type threadUnsafeSet[T comparable] map[T]struct{} 38 | 39 | // Assert concrete type:threadUnsafeSet adheres to Set interface. 40 | var _ Set[string] = (*threadUnsafeSet[string])(nil) 41 | 42 | func newThreadUnsafeSet[T comparable]() *threadUnsafeSet[T] { 43 | t := make(threadUnsafeSet[T]) 44 | return &t 45 | } 46 | 47 | func newThreadUnsafeSetWithSize[T comparable](cardinality int) *threadUnsafeSet[T] { 48 | t := make(threadUnsafeSet[T], cardinality) 49 | return &t 50 | } 51 | 52 | func (s threadUnsafeSet[T]) Add(v T) bool { 53 | prevLen := len(s) 54 | s[v] = struct{}{} 55 | return prevLen != len(s) 56 | } 57 | 58 | func (s *threadUnsafeSet[T]) Append(v ...T) int { 59 | prevLen := len(*s) 60 | for _, val := range v { 61 | (*s)[val] = struct{}{} 62 | } 63 | return len(*s) - prevLen 64 | } 65 | 66 | // private version of Add which doesn't return a value 67 | func (s *threadUnsafeSet[T]) add(v T) { 68 | (*s)[v] = struct{}{} 69 | } 70 | 71 | func (s *threadUnsafeSet[T]) Cardinality() int { 72 | return len(*s) 73 | } 74 | 75 | func (s *threadUnsafeSet[T]) Clear() { 76 | // Constructions like this are optimised by compiler, and replaced by 77 | // mapclear() function, defined in 78 | // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993) 79 | for key := range *s { 80 | delete(*s, key) 81 | } 82 | } 83 | 84 | func (s *threadUnsafeSet[T]) Clone() Set[T] { 85 | clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) 86 | for elem := range *s { 87 | clonedSet.add(elem) 88 | } 89 | return clonedSet 90 | } 91 | 92 | func (s *threadUnsafeSet[T]) Contains(v ...T) bool { 93 | for _, val := range v { 94 | if _, ok := (*s)[val]; !ok { 95 | return false 96 | } 97 | } 98 | return true 99 | } 100 | 101 | func (s *threadUnsafeSet[T]) ContainsOne(v T) bool { 102 | _, ok := (*s)[v] 103 | return ok 104 | } 105 | 106 | func (s *threadUnsafeSet[T]) ContainsAny(v ...T) bool { 107 | for _, val := range v { 108 | if _, ok := (*s)[val]; ok { 109 | return true 110 | } 111 | } 112 | return false 113 | } 114 | 115 | func (s *threadUnsafeSet[T]) ContainsAnyElement(other Set[T]) bool { 116 | o := other.(*threadUnsafeSet[T]) 117 | 118 | // loop over smaller set 119 | if s.Cardinality() < other.Cardinality() { 120 | for elem := range *s { 121 | if o.contains(elem) { 122 | return true 123 | } 124 | } 125 | } else { 126 | for elem := range *o { 127 | if s.contains(elem) { 128 | return true 129 | } 130 | } 131 | } 132 | return false 133 | } 134 | 135 | // private version of Contains for a single element v 136 | func (s *threadUnsafeSet[T]) contains(v T) (ok bool) { 137 | _, ok = (*s)[v] 138 | return ok 139 | } 140 | 141 | func (s *threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { 142 | o := other.(*threadUnsafeSet[T]) 143 | 144 | diff := newThreadUnsafeSet[T]() 145 | for elem := range *s { 146 | if !o.contains(elem) { 147 | diff.add(elem) 148 | } 149 | } 150 | return diff 151 | } 152 | 153 | func (s *threadUnsafeSet[T]) Each(cb func(T) bool) { 154 | for elem := range *s { 155 | if cb(elem) { 156 | break 157 | } 158 | } 159 | } 160 | 161 | func (s *threadUnsafeSet[T]) Equal(other Set[T]) bool { 162 | o := other.(*threadUnsafeSet[T]) 163 | 164 | if s.Cardinality() != other.Cardinality() { 165 | return false 166 | } 167 | for elem := range *s { 168 | if !o.contains(elem) { 169 | return false 170 | } 171 | } 172 | return true 173 | } 174 | 175 | func (s *threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { 176 | o := other.(*threadUnsafeSet[T]) 177 | 178 | intersection := newThreadUnsafeSet[T]() 179 | // loop over smaller set 180 | if s.Cardinality() < other.Cardinality() { 181 | for elem := range *s { 182 | if o.contains(elem) { 183 | intersection.add(elem) 184 | } 185 | } 186 | } else { 187 | for elem := range *o { 188 | if s.contains(elem) { 189 | intersection.add(elem) 190 | } 191 | } 192 | } 193 | return intersection 194 | } 195 | 196 | func (s *threadUnsafeSet[T]) IsEmpty() bool { 197 | return s.Cardinality() == 0 198 | } 199 | 200 | func (s *threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { 201 | return s.Cardinality() < other.Cardinality() && s.IsSubset(other) 202 | } 203 | 204 | func (s *threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool { 205 | return s.Cardinality() > other.Cardinality() && s.IsSuperset(other) 206 | } 207 | 208 | func (s *threadUnsafeSet[T]) IsSubset(other Set[T]) bool { 209 | o := other.(*threadUnsafeSet[T]) 210 | if s.Cardinality() > other.Cardinality() { 211 | return false 212 | } 213 | for elem := range *s { 214 | if !o.contains(elem) { 215 | return false 216 | } 217 | } 218 | return true 219 | } 220 | 221 | func (s *threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { 222 | return other.IsSubset(s) 223 | } 224 | 225 | func (s *threadUnsafeSet[T]) Iter() <-chan T { 226 | ch := make(chan T) 227 | go func() { 228 | for elem := range *s { 229 | ch <- elem 230 | } 231 | close(ch) 232 | }() 233 | 234 | return ch 235 | } 236 | 237 | func (s *threadUnsafeSet[T]) Iterator() *Iterator[T] { 238 | iterator, ch, stopCh := newIterator[T]() 239 | 240 | go func() { 241 | L: 242 | for elem := range *s { 243 | select { 244 | case <-stopCh: 245 | break L 246 | case ch <- elem: 247 | } 248 | } 249 | close(ch) 250 | }() 251 | 252 | return iterator 253 | } 254 | 255 | // Pop returns a popped item in case set is not empty, or nil-value of T 256 | // if set is already empty 257 | func (s *threadUnsafeSet[T]) Pop() (v T, ok bool) { 258 | for item := range *s { 259 | delete(*s, item) 260 | return item, true 261 | } 262 | return v, false 263 | } 264 | 265 | func (s *threadUnsafeSet[T]) PopN(n int) (items []T, count int) { 266 | if n <= 0 || len(*s) == 0 { 267 | return make([]T, 0), 0 268 | } 269 | sn := s.Cardinality() 270 | if n > sn { 271 | n = sn 272 | } 273 | 274 | items = make([]T, 0, sn) 275 | for item := range *s { 276 | if count >= n { 277 | break 278 | } 279 | delete(*s, item) 280 | items = append(items, item) 281 | count++ 282 | } 283 | return items, count 284 | } 285 | 286 | func (s threadUnsafeSet[T]) Remove(v T) { 287 | delete(s, v) 288 | } 289 | 290 | func (s threadUnsafeSet[T]) RemoveAll(i ...T) { 291 | for _, elem := range i { 292 | delete(s, elem) 293 | } 294 | } 295 | 296 | func (s threadUnsafeSet[T]) String() string { 297 | items := make([]string, 0, len(s)) 298 | 299 | for elem := range s { 300 | items = append(items, fmt.Sprintf("%v", elem)) 301 | } 302 | return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) 303 | } 304 | 305 | func (s *threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { 306 | o := other.(*threadUnsafeSet[T]) 307 | 308 | sd := newThreadUnsafeSet[T]() 309 | for elem := range *s { 310 | if !o.contains(elem) { 311 | sd.add(elem) 312 | } 313 | } 314 | for elem := range *o { 315 | if !s.contains(elem) { 316 | sd.add(elem) 317 | } 318 | } 319 | return sd 320 | } 321 | 322 | func (s threadUnsafeSet[T]) ToSlice() []T { 323 | keys := make([]T, 0, s.Cardinality()) 324 | for elem := range s { 325 | keys = append(keys, elem) 326 | } 327 | 328 | return keys 329 | } 330 | 331 | func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { 332 | o := other.(*threadUnsafeSet[T]) 333 | 334 | n := s.Cardinality() 335 | if o.Cardinality() > n { 336 | n = o.Cardinality() 337 | } 338 | unionedSet := make(threadUnsafeSet[T], n) 339 | 340 | for elem := range s { 341 | unionedSet.add(elem) 342 | } 343 | for elem := range *o { 344 | unionedSet.add(elem) 345 | } 346 | return &unionedSet 347 | } 348 | 349 | // MarshalJSON creates a JSON array from the set, it marshals all elements 350 | func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { 351 | items := make([]string, 0, s.Cardinality()) 352 | 353 | for elem := range s { 354 | b, err := json.Marshal(elem) 355 | if err != nil { 356 | return nil, err 357 | } 358 | 359 | items = append(items, string(b)) 360 | } 361 | 362 | return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil 363 | } 364 | 365 | // UnmarshalJSON recreates a set from a JSON array, it only decodes 366 | // primitive types. Numbers are decoded as json.Number. 367 | func (s *threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { 368 | var i []T 369 | err := json.Unmarshal(b, &i) 370 | if err != nil { 371 | return err 372 | } 373 | s.Append(i...) 374 | 375 | return nil 376 | } 377 | 378 | // MarshalBSON creates a BSON array from the set. 379 | func (s threadUnsafeSet[T]) MarshalBSONValue() (bsontype.Type, []byte, error) { 380 | return bson.MarshalValue(s.ToSlice()) 381 | } 382 | 383 | // UnmarshalBSON recreates a set from a BSON array. 384 | func (s threadUnsafeSet[T]) UnmarshalBSONValue(bt bsontype.Type, b []byte) error { 385 | if bt != bson.TypeArray { 386 | return fmt.Errorf("must use BSON Array to unmarshal Set") 387 | } 388 | 389 | var i []T 390 | err := bson.UnmarshalValue(bt, b, &i) 391 | if err != nil { 392 | return err 393 | } 394 | s.Append(i...) 395 | 396 | return nil 397 | } 398 | -------------------------------------------------------------------------------- /threadunsafe_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Open Source Initiative OSI - The MIT License (MIT):Licensing 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | package mapset 27 | 28 | import ( 29 | "encoding/json" 30 | "testing" 31 | ) 32 | 33 | func TestThreadUnsafeSet_MarshalJSON(t *testing.T) { 34 | expected := NewThreadUnsafeSet[int64](1, 2, 3) 35 | actual := newThreadUnsafeSet[int64]() 36 | 37 | // test Marshal from Set method 38 | b, err := expected.MarshalJSON() 39 | if err != nil { 40 | t.Errorf("Error should be nil: %v", err) 41 | } 42 | 43 | err = json.Unmarshal(b, actual) 44 | if err != nil { 45 | t.Errorf("Error should be nil: %v", err) 46 | } 47 | 48 | if !expected.Equal(actual) { 49 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 50 | } 51 | 52 | // test Marshal from json package 53 | b, err = json.Marshal(expected) 54 | if err != nil { 55 | t.Errorf("Error should be nil: %v", err) 56 | } 57 | 58 | err = json.Unmarshal(b, actual) 59 | if err != nil { 60 | t.Errorf("Error should be nil: %v", err) 61 | } 62 | 63 | if !expected.Equal(actual) { 64 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 65 | } 66 | } 67 | 68 | func TestThreadUnsafeSet_UnmarshalJSON(t *testing.T) { 69 | expected := NewThreadUnsafeSet[int64](1, 2, 3) 70 | actual := NewThreadUnsafeSet[int64]() 71 | 72 | // test Unmarshal from Set method 73 | err := actual.UnmarshalJSON([]byte(`[1, 2, 3]`)) 74 | if err != nil { 75 | t.Errorf("Error should be nil: %v", err) 76 | } 77 | if !expected.Equal(actual) { 78 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 79 | } 80 | 81 | // test Unmarshal from json package 82 | actual = NewThreadUnsafeSet[int64]() 83 | err = json.Unmarshal([]byte(`[1, 2, 3]`), actual) 84 | if err != nil { 85 | t.Errorf("Error should be nil: %v", err) 86 | } 87 | if !expected.Equal(actual) { 88 | t.Errorf("Expected no difference, got: %v", expected.Difference(actual)) 89 | } 90 | } 91 | 92 | func TestThreadUnsafeSet_MarshalJSON_Struct(t *testing.T) { 93 | expected := &testStruct{"test", NewThreadUnsafeSet("a")} 94 | 95 | b, err := json.Marshal(&testStruct{"test", NewThreadUnsafeSet("a")}) 96 | if err != nil { 97 | t.Errorf("Error should be nil: %v", err) 98 | } 99 | 100 | actual := &testStruct{} 101 | err = json.Unmarshal(b, actual) 102 | if err != nil { 103 | t.Errorf("Error should be nil: %v", err) 104 | } 105 | 106 | if !expected.Set.Equal(actual.Set) { 107 | t.Errorf("Expected no difference, got: %v", expected.Set.Difference(actual.Set)) 108 | } 109 | } 110 | func TestThreadUnsafeSet_UnmarshalJSON_Struct(t *testing.T) { 111 | expected := &testStruct{"test", NewThreadUnsafeSet("a", "b", "c")} 112 | actual := &testStruct{} 113 | 114 | err := json.Unmarshal([]byte(`{"other":"test", "set":["a", "b", "c"]}`), actual) 115 | if err != nil { 116 | t.Errorf("Error should be nil: %v", err) 117 | } 118 | if !expected.Set.Equal(actual.Set) { 119 | t.Errorf("Expected no difference, got: %v", expected.Set.Difference(actual.Set)) 120 | } 121 | 122 | expectedComplex := NewThreadUnsafeSet(struct{ Val string }{Val: "a"}, struct{ Val string }{Val: "b"}) 123 | actualComplex := NewThreadUnsafeSet[struct{ Val string }]() 124 | 125 | err = actualComplex.UnmarshalJSON([]byte(`[{"Val": "a"}, {"Val": "b"}]`)) 126 | if err != nil { 127 | t.Errorf("Error should be nil: %v", err) 128 | } 129 | if !expectedComplex.Equal(actualComplex) { 130 | t.Errorf("Expected no difference, got: %v", expectedComplex.Difference(actualComplex)) 131 | } 132 | 133 | actualComplex = NewThreadUnsafeSet[struct{ Val string }]() 134 | err = json.Unmarshal([]byte(`[{"Val": "a"}, {"Val": "b"}]`), actualComplex) 135 | if err != nil { 136 | t.Errorf("Error should be nil: %v", err) 137 | } 138 | if !expectedComplex.Equal(actualComplex) { 139 | t.Errorf("Expected no difference, got: %v", expectedComplex.Difference(actualComplex)) 140 | } 141 | } 142 | 143 | // this serves as an example of how to correctly unmarshal a struct with a Set property 144 | type testStruct struct { 145 | Other string 146 | Set Set[string] 147 | } 148 | 149 | func (t *testStruct) UnmarshalJSON(b []byte) error { 150 | raw := struct { 151 | Other string 152 | Set []string 153 | }{} 154 | 155 | err := json.Unmarshal(b, &raw) 156 | if err != nil { 157 | return err 158 | } 159 | 160 | t.Other = raw.Other 161 | t.Set = NewThreadUnsafeSet(raw.Set...) 162 | 163 | return nil 164 | } 165 | --------------------------------------------------------------------------------