New Upstream Release - golang-github-deckarep-golang-set
Ready changes
Summary
Merged new upstream version: 2.3.0 (was: 2.1.0).
Resulting package
Built on 2023-05-16T05:39 (took 4m43s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases golang-github-deckarep-golang-set-dev
Lintian Result
Diff
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..3993573
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,22 @@
+on: [push, pull_request]
+name: CI
+jobs:
+ test:
+ strategy:
+ matrix:
+ go-version: [1.20.1]
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Install Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: ${{ matrix.go-version }}
+ stable: false
+ - name: Checkout code
+ uses: actions/checkout@v3
+ - name: Test
+ run: |
+ go test -v -race ./...
+ # go vet ./...
+ # go test -bench=.
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index db1359c..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-language: go
-
-go:
- - 1.2
-
-script:
- - go test ./...
- #- go test -race ./...
-
diff --git a/LICENSE b/LICENSE
index b5768f8..efd4827 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/README.md b/README.md
index 564d904..55e30af 100644
--- a/README.md
+++ b/README.md
@@ -1,94 +1,173 @@
-[![Build Status](https://travis-ci.org/deckarep/golang-set.png?branch=master)](https://travis-ci.org/deckarep/golang-set)
-[![GoDoc](https://godoc.org/github.com/deckarep/golang-set?status.png)](http://godoc.org/github.com/deckarep/golang-set)
+![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg)
+[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2)
+[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2)
-## golang-set
+# golang-set
+The missing `generic` set collection for the Go language. Until Go has sets built-in...use this.
-The missing set collection for the Go language. Until Go has sets built-in...use this.
+## Update 3/5/2023
+* 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`.
+* supports `new generic` syntax
+* Go `1.18.0` or higher
+* Workflow tested on Go `1.20`
-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 from Python.
-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:
+![With Generics](new_improved.jpeg)
-* Helping to make more idiomatic improvements to the code.
-* Helping to increase the performance of it. ~~(So far, no attempt has been made, but since it uses a map internally, I expect it to be mostly performant.)~~
-* Helping to make the unit-tests more robust and kick-ass.
-* Helping to fill in the [documentation.](http://godoc.org/github.com/deckarep/golang-set)
-* Simply offering feedback and suggestions. (Positive, constructive feedback is appreciated.)
+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.
+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.
-I have to give some credit for helping seed the idea with this post on [stackoverflow.](http://programmers.stackexchange.com/questions/177428/sets-data-structure-in-golang)
+## Features
-*Update* - as of 3/9/2014, you can use a compile-time generic version of this package in the [gen](http://clipperhouse.github.io/gen/) framework. This framework allows you to use the golang-set in a completely generic and type-safe way by allowing you to generate a supporting .go file based on your custom types.
+* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher)
+* One common *interface* to both implementations
+ * a **non threadsafe** implementation favoring *performance*
+ * a **threadsafe** implementation favoring *concurrent* use
+* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set).
+* Exhaustive unit-test and benchmark suite
-## Features (as of 9/22/2014)
+## Trusted by
-* a CartesianProduct() method has been added with unit-tests: [Read more about the cartesian product](http://en.wikipedia.org/wiki/Cartesian_product)
+This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package.
-## Features (as of 9/15/2014)
+* Notable projects/companies using this package
+ * Ethereum
+ * Docker
+ * 1Password
+ * Hashicorp
-* a PowerSet() method has been added with unit-tests: [Read more about the Power set](http://en.wikipedia.org/wiki/Power_set)
+## Star History
-## Features (as of 4/22/2014)
+[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date)
-* One common interface to both implementations
-* Two set implementations to choose from
- * a thread-safe implementation designed for concurrent use
- * a non-thread-safe implementation designed for performance
-* 75 benchmarks for both implementations
-* 35 unit tests for both implementations
-* 14 concurrent tests for the thread-safe implementation
+## Usage
+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.
-Please see the unit test file for additional usage examples. The Python set documentation will also do a better job than I can of explaining how a set typically [works.](http://docs.python.org/2/library/sets.html) Please keep in mind
-however that the Python set is a built-in type and supports additional features and syntax that make it awesome.
+What is considered comparable in Go?
+* `Booleans`, `integers`, `strings`, `floats` or basically primitive types.
+* `Pointers`
+* `Arrays`
+* `Structs` if *all of their fields* are also comparable independently
-## Examples but not exhaustive:
+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.
```go
-requiredClasses := mapset.NewSet()
-requiredClasses.Add("Cooking")
-requiredClasses.Add("English")
-requiredClasses.Add("Math")
-requiredClasses.Add("Biology")
+// Syntax example, doesn't compile.
+mySet := mapset.NewSet[T]() // where T is some concrete comparable type.
-scienceSlice := []interface{}{"Biology", "Chemistry"}
-scienceClasses := mapset.NewSetFromSlice(scienceSlice)
+// Therefore this code creates an int set
+mySet := mapset.NewSet[int]()
-electiveClasses := mapset.NewSet()
-electiveClasses.Add("Welding")
-electiveClasses.Add("Music")
-electiveClasses.Add("Automotive")
+// Or perhaps you want a string set
+mySet := mapset.NewSet[string]()
-bonusClasses := mapset.NewSet()
-bonusClasses.Add("Go Programming")
-bonusClasses.Add("Python Programming")
+type myStruct {
+ name string
+ age uint8
+}
-//Show me all the available classes I can take
-allClasses := requiredClasses.Union(scienceClasses).Union(electiveClasses).Union(bonusClasses)
-fmt.Println(allClasses) //Set{Cooking, English, Math, Chemistry, Welding, Biology, Music, Automotive, Go Programming, Python Programming}
+// Alternatively a set of structs
+mySet := mapset.NewSet[myStruct]()
+// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety.
+mySet := mapset.NewSet[any]()
+```
+
+## Comprehensive Example
+
+```go
+package main
+
+import (
+ "fmt"
+ mapset "github.com/deckarep/golang-set/v2"
+)
+
+func main() {
+ // Create a string-based set of required classes.
+ required := mapset.NewSet[string]()
+ required.Add("cooking")
+ required.Add("english")
+ required.Add("math")
+ required.Add("biology")
+
+ // Create a string-based set of science classes.
+ sciences := mapset.NewSet[string]()
+ sciences.Add("biology")
+ sciences.Add("chemistry")
+
+ // Create a string-based set of electives.
+ electives := mapset.NewSet[string]()
+ electives.Add("welding")
+ electives.Add("music")
+ electives.Add("automotive")
+
+ // Create a string-based set of bonus programming classes.
+ bonus := mapset.NewSet[string]()
+ bonus.Add("beginner go")
+ bonus.Add("python for dummies")
+}
+```
+
+Create a set of all unique classes.
+Sets will *automatically* deduplicate the same data.
+
+```go
+ all := required
+ .Union(sciences)
+ .Union(electives)
+ .Union(bonus)
+
+ fmt.Println(all)
+```
+
+Output:
+```sh
+Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies}
+```
-//Is cooking considered a science class?
-fmt.Println(scienceClasses.Contains("Cooking")) //false
+Is cooking considered a science class?
+```go
+result := sciences.Contains("cooking")
+fmt.Println(result)
+```
-//Show me all classes that are not science classes, since I hate science.
-fmt.Println(allClasses.Difference(scienceClasses)) //Set{Music, Automotive, Go Programming, Python Programming, Cooking, English, Math, Welding}
+Output:
+```false
+false
+```
-//Which science classes are also required classes?
-fmt.Println(scienceClasses.Intersect(requiredClasses)) //Set{Biology}
+Show me all classes that are not science classes, since I don't enjoy science.
+```go
+notScience := all.Difference(sciences)
+fmt.Println(notScience)
+```
-//How many bonus classes do you offer?
-fmt.Println(bonusClasses.Cardinality()) //2
+```sh
+Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding }
+```
-//Do you have the following classes? Welding, Automotive and English?
-fmt.Println(allClasses.IsSuperset(mapset.NewSetFromSlice([]interface{}{"Welding", "Automotive", "English"}))) //true
+Which science classes are also required classes?
+```go
+reqScience := sciences.Intersect(required)
```
-Thanks!
+Output:
+```sh
+Set{biology}
+```
--Ralph
+How many bonus classes do you offer?
+```go
+fmt.Println(bonus.Cardinality())
+```
+Output:
+```sh
+2
+```
-[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/deckarep/golang-set/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
+Thanks for visiting!
-[![Analytics](https://ga-beacon.appspot.com/UA-42584447-2/deckarep/golang-set)](https://github.com/igrigorik/ga-beacon)
+-deckarep
diff --git a/bench_test.go b/bench_test.go
index a5b5fe3..5d4744a 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -1,3 +1,28 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
package mapset
import (
@@ -13,31 +38,26 @@ func nrand(n int) []int {
return i
}
-func toInterfaces(i []int) []interface{} {
- ifs := make([]interface{}, len(i))
- for ind, v := range i {
- ifs[ind] = v
- }
- return ifs
-}
-
-func benchAdd(b *testing.B, s Set) {
- nums := nrand(b.N)
+func benchAdd(b *testing.B, n int, newSet func(...int) Set[int]) {
+ nums := nrand(n)
b.ResetTimer()
- for _, v := range nums {
- s.Add(v)
+ for i := 0; i < b.N; i++ {
+ s := newSet()
+ for _, v := range nums {
+ s.Add(v)
+ }
}
}
func BenchmarkAddSafe(b *testing.B) {
- benchAdd(b, NewSet())
+ benchAdd(b, 1000, NewSet[int])
}
func BenchmarkAddUnsafe(b *testing.B) {
- benchAdd(b, NewThreadUnsafeSet())
+ benchAdd(b, 1000, NewThreadUnsafeSet[int])
}
-func benchRemove(b *testing.B, s Set) {
+func benchRemove(b *testing.B, s Set[int]) {
nums := nrand(b.N)
for _, v := range nums {
s.Add(v)
@@ -50,28 +70,28 @@ func benchRemove(b *testing.B, s Set) {
}
func BenchmarkRemoveSafe(b *testing.B) {
- benchRemove(b, NewSet())
+ benchRemove(b, NewSet[int]())
}
func BenchmarkRemoveUnsafe(b *testing.B) {
- benchRemove(b, NewThreadUnsafeSet())
+ benchRemove(b, NewThreadUnsafeSet[int]())
}
-func benchCardinality(b *testing.B, s Set) {
+func benchCardinality(b *testing.B, s Set[int]) {
for i := 0; i < b.N; i++ {
s.Cardinality()
}
}
func BenchmarkCardinalitySafe(b *testing.B) {
- benchCardinality(b, NewSet())
+ benchCardinality(b, NewSet[int]())
}
func BenchmarkCardinalityUnsafe(b *testing.B) {
- benchCardinality(b, NewThreadUnsafeSet())
+ benchCardinality(b, NewThreadUnsafeSet[int]())
}
-func benchClear(b *testing.B, s Set) {
+func benchClear(b *testing.B, s Set[int]) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.Clear()
@@ -79,15 +99,15 @@ func benchClear(b *testing.B, s Set) {
}
func BenchmarkClearSafe(b *testing.B) {
- benchClear(b, NewSet())
+ benchClear(b, NewSet[int]())
}
func BenchmarkClearUnsafe(b *testing.B) {
- benchClear(b, NewThreadUnsafeSet())
+ benchClear(b, NewThreadUnsafeSet[int]())
}
-func benchClone(b *testing.B, n int, s Set) {
- nums := toInterfaces(nrand(n))
+func benchClone(b *testing.B, n int, s Set[int]) {
+ nums := nrand(n)
for _, v := range nums {
s.Add(v)
}
@@ -99,31 +119,31 @@ func benchClone(b *testing.B, n int, s Set) {
}
func BenchmarkClone1Safe(b *testing.B) {
- benchClone(b, 1, NewSet())
+ benchClone(b, 1, NewSet[int]())
}
func BenchmarkClone1Unsafe(b *testing.B) {
- benchClone(b, 1, NewThreadUnsafeSet())
+ benchClone(b, 1, NewThreadUnsafeSet[int]())
}
func BenchmarkClone10Safe(b *testing.B) {
- benchClone(b, 10, NewSet())
+ benchClone(b, 10, NewSet[int]())
}
func BenchmarkClone10Unsafe(b *testing.B) {
- benchClone(b, 10, NewThreadUnsafeSet())
+ benchClone(b, 10, NewThreadUnsafeSet[int]())
}
func BenchmarkClone100Safe(b *testing.B) {
- benchClone(b, 100, NewSet())
+ benchClone(b, 100, NewSet[int]())
}
func BenchmarkClone100Unsafe(b *testing.B) {
- benchClone(b, 100, NewThreadUnsafeSet())
+ benchClone(b, 100, NewThreadUnsafeSet[int]())
}
-func benchContains(b *testing.B, n int, s Set) {
- nums := toInterfaces(nrand(n))
+func benchContains(b *testing.B, n int, s Set[int]) {
+ nums := nrand(n)
for _, v := range nums {
s.Add(v)
}
@@ -137,30 +157,30 @@ func benchContains(b *testing.B, n int, s Set) {
}
func BenchmarkContains1Safe(b *testing.B) {
- benchContains(b, 1, NewSet())
+ benchContains(b, 1, NewSet[int]())
}
func BenchmarkContains1Unsafe(b *testing.B) {
- benchContains(b, 1, NewThreadUnsafeSet())
+ benchContains(b, 1, NewThreadUnsafeSet[int]())
}
func BenchmarkContains10Safe(b *testing.B) {
- benchContains(b, 10, NewSet())
+ benchContains(b, 10, NewSet[int]())
}
func BenchmarkContains10Unsafe(b *testing.B) {
- benchContains(b, 10, NewThreadUnsafeSet())
+ benchContains(b, 10, NewThreadUnsafeSet[int]())
}
func BenchmarkContains100Safe(b *testing.B) {
- benchContains(b, 100, NewSet())
+ benchContains(b, 100, NewSet[int]())
}
func BenchmarkContains100Unsafe(b *testing.B) {
- benchContains(b, 100, NewThreadUnsafeSet())
+ benchContains(b, 100, NewThreadUnsafeSet[int]())
}
-func benchEqual(b *testing.B, n int, s, t Set) {
+func benchEqual(b *testing.B, n int, s, t Set[int]) {
nums := nrand(n)
for _, v := range nums {
s.Add(v)
@@ -174,30 +194,30 @@ func benchEqual(b *testing.B, n int, s, t Set) {
}
func BenchmarkEqual1Safe(b *testing.B) {
- benchEqual(b, 1, NewSet(), NewSet())
+ benchEqual(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkEqual1Unsafe(b *testing.B) {
- benchEqual(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchEqual(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkEqual10Safe(b *testing.B) {
- benchEqual(b, 10, NewSet(), NewSet())
+ benchEqual(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkEqual10Unsafe(b *testing.B) {
- benchEqual(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchEqual(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkEqual100Safe(b *testing.B) {
- benchEqual(b, 100, NewSet(), NewSet())
+ benchEqual(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkEqual100Unsafe(b *testing.B) {
- benchEqual(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchEqual(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
-func benchDifference(b *testing.B, n int, s, t Set) {
+func benchDifference(b *testing.B, n int, s, t Set[int]) {
nums := nrand(n)
for _, v := range nums {
s.Add(v)
@@ -212,7 +232,7 @@ func benchDifference(b *testing.B, n int, s, t Set) {
}
}
-func benchIsSubset(b *testing.B, n int, s, t Set) {
+func benchIsSubset(b *testing.B, n int, s, t Set[int]) {
nums := nrand(n)
for _, v := range nums {
s.Add(v)
@@ -226,30 +246,30 @@ func benchIsSubset(b *testing.B, n int, s, t Set) {
}
func BenchmarkIsSubset1Safe(b *testing.B) {
- benchIsSubset(b, 1, NewSet(), NewSet())
+ benchIsSubset(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkIsSubset1Unsafe(b *testing.B) {
- benchIsSubset(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIsSubset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkIsSubset10Safe(b *testing.B) {
- benchIsSubset(b, 10, NewSet(), NewSet())
+ benchIsSubset(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkIsSubset10Unsafe(b *testing.B) {
- benchIsSubset(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIsSubset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkIsSubset100Safe(b *testing.B) {
- benchIsSubset(b, 100, NewSet(), NewSet())
+ benchIsSubset(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkIsSubset100Unsafe(b *testing.B) {
- benchIsSubset(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIsSubset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
-func benchIsSuperset(b *testing.B, n int, s, t Set) {
+func benchIsSuperset(b *testing.B, n int, s, t Set[int]) {
nums := nrand(n)
for _, v := range nums {
s.Add(v)
@@ -263,54 +283,128 @@ func benchIsSuperset(b *testing.B, n int, s, t Set) {
}
func BenchmarkIsSuperset1Safe(b *testing.B) {
- benchIsSuperset(b, 1, NewSet(), NewSet())
+ benchIsSuperset(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkIsSuperset1Unsafe(b *testing.B) {
- benchIsSuperset(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIsSuperset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkIsSuperset10Safe(b *testing.B) {
- benchIsSuperset(b, 10, NewSet(), NewSet())
+ benchIsSuperset(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkIsSuperset10Unsafe(b *testing.B) {
- benchIsSuperset(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIsSuperset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkIsSuperset100Safe(b *testing.B) {
- benchIsSuperset(b, 100, NewSet(), NewSet())
+ benchIsSuperset(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkIsSuperset100Unsafe(b *testing.B) {
- benchIsSuperset(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIsSuperset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func benchIsProperSubset(b *testing.B, n int, s, t Set[int]) {
+ nums := nrand(n)
+ for _, v := range nums {
+ s.Add(v)
+ t.Add(v)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ s.IsProperSubset(t)
+ }
+}
+
+func BenchmarkIsProperSubset1Safe(b *testing.B) {
+ benchIsProperSubset(b, 1, NewSet[int](), NewSet[int]())
+}
+
+func BenchmarkIsProperSubset1Unsafe(b *testing.B) {
+ benchIsProperSubset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkIsProperSubset10Safe(b *testing.B) {
+ benchIsProperSubset(b, 10, NewSet[int](), NewSet[int]())
+}
+
+func BenchmarkIsProperSubset10Unsafe(b *testing.B) {
+ benchIsProperSubset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkIsProperSubset100Safe(b *testing.B) {
+ benchIsProperSubset(b, 100, NewSet[int](), NewSet[int]())
+}
+
+func BenchmarkIsProperSubset100Unsafe(b *testing.B) {
+ benchIsProperSubset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func benchIsProperSuperset(b *testing.B, n int, s, t Set[int]) {
+ nums := nrand(n)
+ for _, v := range nums {
+ s.Add(v)
+ t.Add(v)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ s.IsProperSuperset(t)
+ }
+}
+
+func BenchmarkIsProperSuperset1Safe(b *testing.B) {
+ benchIsProperSuperset(b, 1, NewSet[int](), NewSet[int]())
+}
+
+func BenchmarkIsProperSuperset1Unsafe(b *testing.B) {
+ benchIsProperSuperset(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkIsProperSuperset10Safe(b *testing.B) {
+ benchIsProperSuperset(b, 10, NewSet[int](), NewSet[int]())
+}
+
+func BenchmarkIsProperSuperset10Unsafe(b *testing.B) {
+ benchIsProperSuperset(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkIsProperSuperset100Safe(b *testing.B) {
+ benchIsProperSuperset(b, 100, NewSet[int](), NewSet[int]())
+}
+
+func BenchmarkIsProperSuperset100Unsafe(b *testing.B) {
+ benchIsProperSuperset(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkDifference1Safe(b *testing.B) {
- benchDifference(b, 1, NewSet(), NewSet())
+ benchDifference(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkDifference1Unsafe(b *testing.B) {
- benchDifference(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchDifference(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkDifference10Safe(b *testing.B) {
- benchDifference(b, 10, NewSet(), NewSet())
+ benchDifference(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkDifference10Unsafe(b *testing.B) {
- benchDifference(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchDifference(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkDifference100Safe(b *testing.B) {
- benchDifference(b, 100, NewSet(), NewSet())
+ benchDifference(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkDifference100Unsafe(b *testing.B) {
- benchDifference(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchDifference(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
-func benchIntersect(b *testing.B, n int, s, t Set) {
+func benchIntersect(b *testing.B, n int, s, t Set[int]) {
nums := nrand(int(float64(n) * float64(1.5)))
for _, v := range nums[:n] {
s.Add(v)
@@ -326,30 +420,30 @@ func benchIntersect(b *testing.B, n int, s, t Set) {
}
func BenchmarkIntersect1Safe(b *testing.B) {
- benchIntersect(b, 1, NewSet(), NewSet())
+ benchIntersect(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkIntersect1Unsafe(b *testing.B) {
- benchIntersect(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIntersect(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkIntersect10Safe(b *testing.B) {
- benchIntersect(b, 10, NewSet(), NewSet())
+ benchIntersect(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkIntersect10Unsafe(b *testing.B) {
- benchIntersect(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIntersect(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkIntersect100Safe(b *testing.B) {
- benchIntersect(b, 100, NewSet(), NewSet())
+ benchIntersect(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkIntersect100Unsafe(b *testing.B) {
- benchIntersect(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchIntersect(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
-func benchSymmetricDifference(b *testing.B, n int, s, t Set) {
+func benchSymmetricDifference(b *testing.B, n int, s, t Set[int]) {
nums := nrand(int(float64(n) * float64(1.5)))
for _, v := range nums[:n] {
s.Add(v)
@@ -365,30 +459,30 @@ func benchSymmetricDifference(b *testing.B, n int, s, t Set) {
}
func BenchmarkSymmetricDifference1Safe(b *testing.B) {
- benchSymmetricDifference(b, 1, NewSet(), NewSet())
+ benchSymmetricDifference(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkSymmetricDifference1Unsafe(b *testing.B) {
- benchSymmetricDifference(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchSymmetricDifference(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkSymmetricDifference10Safe(b *testing.B) {
- benchSymmetricDifference(b, 10, NewSet(), NewSet())
+ benchSymmetricDifference(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkSymmetricDifference10Unsafe(b *testing.B) {
- benchSymmetricDifference(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchSymmetricDifference(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkSymmetricDifference100Safe(b *testing.B) {
- benchSymmetricDifference(b, 100, NewSet(), NewSet())
+ benchSymmetricDifference(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkSymmetricDifference100Unsafe(b *testing.B) {
- benchSymmetricDifference(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchSymmetricDifference(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
-func benchUnion(b *testing.B, n int, s, t Set) {
+func benchUnion(b *testing.B, n int, s, t Set[int]) {
nums := nrand(n)
for _, v := range nums[:n/2] {
s.Add(v)
@@ -404,30 +498,68 @@ func benchUnion(b *testing.B, n int, s, t Set) {
}
func BenchmarkUnion1Safe(b *testing.B) {
- benchUnion(b, 1, NewSet(), NewSet())
+ benchUnion(b, 1, NewSet[int](), NewSet[int]())
}
func BenchmarkUnion1Unsafe(b *testing.B) {
- benchUnion(b, 1, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchUnion(b, 1, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkUnion10Safe(b *testing.B) {
- benchUnion(b, 10, NewSet(), NewSet())
+ benchUnion(b, 10, NewSet[int](), NewSet[int]())
}
func BenchmarkUnion10Unsafe(b *testing.B) {
- benchUnion(b, 10, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchUnion(b, 10, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
}
func BenchmarkUnion100Safe(b *testing.B) {
- benchUnion(b, 100, NewSet(), NewSet())
+ benchUnion(b, 100, NewSet[int](), NewSet[int]())
}
func BenchmarkUnion100Unsafe(b *testing.B) {
- benchUnion(b, 100, NewThreadUnsafeSet(), NewThreadUnsafeSet())
+ benchUnion(b, 100, NewThreadUnsafeSet[int](), NewThreadUnsafeSet[int]())
+}
+
+func benchEach(b *testing.B, n int, s Set[int]) {
+ nums := nrand(n)
+ for _, v := range nums {
+ s.Add(v)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ s.Each(func(elem int) bool {
+ return false
+ })
+ }
+}
+
+func BenchmarkEach1Safe(b *testing.B) {
+ benchEach(b, 1, NewSet[int]())
+}
+
+func BenchmarkEach1Unsafe(b *testing.B) {
+ benchEach(b, 1, NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkEach10Safe(b *testing.B) {
+ benchEach(b, 10, NewSet[int]())
+}
+
+func BenchmarkEach10Unsafe(b *testing.B) {
+ benchEach(b, 10, NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkEach100Safe(b *testing.B) {
+ benchEach(b, 100, NewSet[int]())
}
-func benchIter(b *testing.B, n int, s Set) {
+func BenchmarkEach100Unsafe(b *testing.B) {
+ benchEach(b, 100, NewThreadUnsafeSet[int]())
+}
+
+func benchIter(b *testing.B, n int, s Set[int]) {
nums := nrand(n)
for _, v := range nums {
s.Add(v)
@@ -436,37 +568,76 @@ func benchIter(b *testing.B, n int, s Set) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
c := s.Iter()
- for _ = range c {
+ for range c {
}
}
}
func BenchmarkIter1Safe(b *testing.B) {
- benchIter(b, 1, NewSet())
+ benchIter(b, 1, NewSet[int]())
}
func BenchmarkIter1Unsafe(b *testing.B) {
- benchIter(b, 1, NewThreadUnsafeSet())
+ benchIter(b, 1, NewThreadUnsafeSet[int]())
}
func BenchmarkIter10Safe(b *testing.B) {
- benchIter(b, 10, NewSet())
+ benchIter(b, 10, NewSet[int]())
}
func BenchmarkIter10Unsafe(b *testing.B) {
- benchIter(b, 10, NewThreadUnsafeSet())
+ benchIter(b, 10, NewThreadUnsafeSet[int]())
}
func BenchmarkIter100Safe(b *testing.B) {
- benchIter(b, 100, NewSet())
+ benchIter(b, 100, NewSet[int]())
}
func BenchmarkIter100Unsafe(b *testing.B) {
- benchIter(b, 100, NewThreadUnsafeSet())
+ benchIter(b, 100, NewThreadUnsafeSet[int]())
+}
+
+func benchIterator(b *testing.B, n int, s Set[int]) {
+ nums := nrand(n)
+ for _, v := range nums {
+ s.Add(v)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c := s.Iterator().C
+ for range c {
+
+ }
+ }
+}
+
+func BenchmarkIterator1Safe(b *testing.B) {
+ benchIterator(b, 1, NewSet[int]())
+}
+
+func BenchmarkIterator1Unsafe(b *testing.B) {
+ benchIterator(b, 1, NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkIterator10Safe(b *testing.B) {
+ benchIterator(b, 10, NewSet[int]())
+}
+
+func BenchmarkIterator10Unsafe(b *testing.B) {
+ benchIterator(b, 10, NewThreadUnsafeSet[int]())
+}
+
+func BenchmarkIterator100Safe(b *testing.B) {
+ benchIterator(b, 100, NewSet[int]())
+}
+
+func BenchmarkIterator100Unsafe(b *testing.B) {
+ benchIterator(b, 100, NewThreadUnsafeSet[int]())
}
-func benchString(b *testing.B, n int, s Set) {
+func benchString(b *testing.B, n int, s Set[int]) {
nums := nrand(n)
for _, v := range nums {
s.Add(v)
@@ -474,35 +645,35 @@ func benchString(b *testing.B, n int, s Set) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
- s.String()
+ _ = s.String()
}
}
func BenchmarkString1Safe(b *testing.B) {
- benchString(b, 1, NewSet())
+ benchString(b, 1, NewSet[int]())
}
func BenchmarkString1Unsafe(b *testing.B) {
- benchString(b, 1, NewThreadUnsafeSet())
+ benchString(b, 1, NewThreadUnsafeSet[int]())
}
func BenchmarkString10Safe(b *testing.B) {
- benchString(b, 10, NewSet())
+ benchString(b, 10, NewSet[int]())
}
func BenchmarkString10Unsafe(b *testing.B) {
- benchString(b, 10, NewThreadUnsafeSet())
+ benchString(b, 10, NewThreadUnsafeSet[int]())
}
func BenchmarkString100Safe(b *testing.B) {
- benchString(b, 100, NewSet())
+ benchString(b, 100, NewSet[int]())
}
func BenchmarkString100Unsafe(b *testing.B) {
- benchString(b, 100, NewThreadUnsafeSet())
+ benchString(b, 100, NewThreadUnsafeSet[int]())
}
-func benchToSlice(b *testing.B, s Set) {
+func benchToSlice(b *testing.B, s Set[int]) {
nums := nrand(b.N)
for _, v := range nums {
s.Add(v)
@@ -515,9 +686,9 @@ func benchToSlice(b *testing.B, s Set) {
}
func BenchmarkToSliceSafe(b *testing.B) {
- benchToSlice(b, NewSet())
+ benchToSlice(b, NewSet[int]())
}
func BenchmarkToSliceUnsafe(b *testing.B) {
- benchToSlice(b, NewThreadUnsafeSet())
+ benchToSlice(b, NewThreadUnsafeSet[int]())
}
diff --git a/debian/changelog b/debian/changelog
index 0746233..1623b33 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,15 @@
-golang-github-deckarep-golang-set (1.5-3) UNRELEASED; urgency=medium
+golang-github-deckarep-golang-set (2.3.0-1) UNRELEASED; urgency=medium
+ [ Tianon Gravi ]
* Team upload.
* Remove self from Uploaders
- -- Tianon Gravi <tianon@debian.org> Wed, 23 Feb 2022 11:11:30 -0800
+ [ Debian Janitor ]
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+
+ -- Tianon Gravi <tianon@debian.org> Tue, 16 May 2023 05:35:42 -0000
golang-github-deckarep-golang-set (1.5-2) unstable; urgency=medium
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ae7f41e
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/deckarep/golang-set/v2
+
+go 1.18
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..e69de29
diff --git a/iterator.go b/iterator.go
new file mode 100644
index 0000000..fc14e70
--- /dev/null
+++ b/iterator.go
@@ -0,0 +1,58 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package mapset
+
+// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's
+// elements.
+type Iterator[T comparable] struct {
+ C <-chan T
+ stop chan struct{}
+}
+
+// Stop stops the Iterator, no further elements will be received on C, C will be closed.
+func (i *Iterator[T]) Stop() {
+ // Allows for Stop() to be called multiple times
+ // (close() panics when called on already closed channel)
+ defer func() {
+ recover()
+ }()
+
+ close(i.stop)
+
+ // Exhaust any remaining elements.
+ for range i.C {
+ }
+}
+
+// newIterator returns a new Iterator instance together with its item and stop channels.
+func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) {
+ itemChan := make(chan T)
+ stopChan := make(chan struct{})
+ return &Iterator[T]{
+ C: itemChan,
+ stop: stopChan,
+ }, itemChan, stopChan
+}
diff --git a/iterator_example_test.go b/iterator_example_test.go
new file mode 100644
index 0000000..a974935
--- /dev/null
+++ b/iterator_example_test.go
@@ -0,0 +1,60 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package mapset
+
+import (
+ "testing"
+)
+
+type yourType struct {
+ name string
+}
+
+func Test_ExampleIterator(t *testing.T) {
+
+ s := NewSet(
+ []*yourType{
+ {name: "Alise"},
+ {name: "Bob"},
+ {name: "John"},
+ {name: "Nick"},
+ }...,
+ )
+
+ var found *yourType
+ it := s.Iterator()
+
+ for elem := range it.C {
+ if elem.name == "John" {
+ found = elem
+ it.Stop()
+ }
+ }
+
+ if found == nil || found.name != "John" {
+ t.Fatalf("expected iterator to have found `John` record but got nil or something else")
+ }
+}
diff --git a/new_improved.jpeg b/new_improved.jpeg
new file mode 100644
index 0000000..429752a
Binary files /dev/null and b/new_improved.jpeg differ
diff --git a/set.go b/set.go
index eccba70..803c8ea 100644
--- a/set.go
+++ b/set.go
@@ -2,7 +2,7 @@
Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -23,37 +23,46 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
-// Package mapset implements a simple and generic set collection.
+// Package mapset implements a simple and set collection.
// Items stored within it are unordered and unique. It supports
// typical set operations: membership testing, intersection, union,
// difference, symmetric difference and cloning.
//
-// Package mapset provides two implementations. The default
-// implementation is safe for concurrent access. There is a non-threadsafe
-// implementation which is slightly more performant.
+// Package mapset provides two implementations of the Set
+// interface. The default implementation is safe for concurrent
+// access, but a non-thread-safe implementation is also provided for
+// programs that can benefit from the slight speed improvement and
+// that can enforce mutual exclusion through other means.
package mapset
-type Set interface {
- // Adds an element to the set. Returns whether
+// Set is the primary interface provided by the mapset package. It
+// represents an unordered set of data and a large number of
+// operations that can be applied to that set.
+type Set[T comparable] interface {
+ // Add adds an element to the set. Returns whether
// the item was added.
- Add(i interface{}) bool
+ Add(val T) bool
- // Returns the number of elements in the set.
+ // Append multiple elements to the set. Returns
+ // the number of elements added.
+ Append(val ...T) int
+
+ // Cardinality returns the number of elements in the set.
Cardinality() int
- // Removes all elements from the set, leaving
- // the emtpy set.
+ // Clear removes all elements from the set, leaving
+ // the empty set.
Clear()
- // Returns a clone of the set using the same
+ // Clone returns a clone of the set using the same
// implementation, duplicating all keys.
- Clone() Set
+ Clone() Set[T]
- // Returns whether the given items
+ // Contains returns whether the given items
// are all in the set.
- Contains(i ...interface{}) bool
+ Contains(val ...T) bool
- // Returns the difference between this set
+ // Difference returns the difference between this set
// and other. The returned set will contain
// all elements of this set that are not also
// elements of other.
@@ -62,9 +71,9 @@ type Set interface {
// must be of the same type as the receiver
// of the method. Otherwise, Difference will
// panic.
- Difference(other Set) Set
+ Difference(other Set[T]) Set[T]
- // Determines if two sets are equal to each
+ // Equal determines if two sets are equal to each
// other. If they have the same cardinality
// and contain the same elements, they are
// considered equal. The order in which
@@ -73,96 +82,160 @@ type Set interface {
// Note that the argument to Equal must be
// of the same type as the receiver of the
// method. Otherwise, Equal will panic.
- Equal(other Set) bool
+ Equal(other Set[T]) bool
- // Returns a new set containing only the elements
+ // Intersect returns a new set containing only the elements
// that exist only in both sets.
//
// Note that the argument to Intersect
// must be of the same type as the receiver
// of the method. Otherwise, Intersect will
// panic.
- Intersect(other Set) Set
+ Intersect(other Set[T]) Set[T]
- // Determines if every element in the other set
- // is in this set.
+ // IsProperSubset determines if every element in this set is in
+ // the other set but the two sets are not equal.
+ //
+ // Note that the argument to IsProperSubset
+ // must be of the same type as the receiver
+ // of the method. Otherwise, IsProperSubset
+ // will panic.
+ IsProperSubset(other Set[T]) bool
+
+ // IsProperSuperset determines if every element in the other set
+ // is in this set but the two sets are not
+ // equal.
+ //
+ // Note that the argument to IsSuperset
+ // must be of the same type as the receiver
+ // of the method. Otherwise, IsSuperset will
+ // panic.
+ IsProperSuperset(other Set[T]) bool
+
+ // IsSubset determines if every element in this set is in
+ // the other set.
//
// Note that the argument to IsSubset
// must be of the same type as the receiver
// of the method. Otherwise, IsSubset will
// panic.
- IsSubset(other Set) bool
+ IsSubset(other Set[T]) bool
- // Determines if every element in this set is in
- // the other set.
+ // IsSuperset determines if every element in the other set
+ // is in this set.
//
// Note that the argument to IsSuperset
// must be of the same type as the receiver
// of the method. Otherwise, IsSuperset will
// panic.
- IsSuperset(other Set) bool
+ IsSuperset(other Set[T]) bool
- // Returns a channel of elements that you can
+ // Each iterates over elements and executes the passed func against each element.
+ // If passed func returns true, stop iteration at the time.
+ Each(func(T) bool)
+
+ // Iter returns a channel of elements that you can
// range over.
- Iter() <-chan interface{}
+ Iter() <-chan T
+
+ // Iterator returns an Iterator object that you can
+ // use to range over the set.
+ Iterator() *Iterator[T]
- // Remove a single element from the set.
- Remove(i interface{})
+ // Remove removes a single element from the set.
+ Remove(i T)
- // Provides a convenient string representation
+ // RemoveAll removes multiple elements from the set.
+ RemoveAll(i ...T)
+
+ // String provides a convenient string representation
// of the current state of the set.
String() string
- // Returns a new set with all elements which are
+ // SymmetricDifference returns a new set with all elements which are
// in either this set or the other set but not in both.
//
// Note that the argument to SymmetricDifference
// must be of the same type as the receiver
// of the method. Otherwise, SymmetricDifference
// will panic.
- SymmetricDifference(other Set) Set
+ SymmetricDifference(other Set[T]) Set[T]
- // Returns a new set with all elements in both sets.
+ // Union returns a new set with all elements in both sets.
//
// Note that the argument to Union must be of the
// same type as the receiver of the method.
// Otherwise, IsSuperset will panic.
- Union(other Set) Set
+ Union(other Set[T]) Set[T]
+
+ // Pop removes and returns an arbitrary item from the set.
+ Pop() (T, bool)
- // Returns all subsets of a given set (Power Set).
- PowerSet() Set
+ // ToSlice returns the members of the set as a slice.
+ ToSlice() []T
- // Returns the Cartesian Product of two sets.
- CartesianProduct(other Set) Set
+ // MarshalJSON will marshal the set into a JSON-based representation.
+ MarshalJSON() ([]byte, error)
- // Returns the members of the set as a slice.
- ToSlice() []interface{}
+ // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure.
+ // For this to work, set subtypes must implemented the Marshal/Unmarshal interface.
+ UnmarshalJSON(b []byte) error
+}
+
+// NewSet creates and returns a new set with the given elements.
+// Operations on the resulting set are thread-safe.
+func NewSet[T comparable](vals ...T) Set[T] {
+ s := newThreadSafeSetWithSize[T](len(vals))
+ for _, item := range vals {
+ s.Add(item)
+ }
+ return s
}
-// Creates and returns a reference to an empty set.
-func NewSet() Set {
- set := newThreadSafeSet()
- return &set
+// NewSetWithSize creates and returns a reference to an empty set with a specified
+// capacity. Operations on the resulting set are thread-safe.
+func NewSetWithSize[T comparable](cardinality int) Set[T] {
+ s := newThreadSafeSetWithSize[T](cardinality)
+ return s
}
-// Creates and returns a reference to a set from an existing slice
-func NewSetFromSlice(s []interface{}) Set {
- a := NewSet()
- for _, item := range s {
- a.Add(item)
+// NewThreadUnsafeSet creates and returns a new set with the given elements.
+// Operations on the resulting set are not thread-safe.
+func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] {
+ s := newThreadUnsafeSetWithSize[T](len(vals))
+ for _, item := range vals {
+ s.Add(item)
}
- return a
+ return s
}
-func NewThreadUnsafeSet() Set {
- set := newThreadUnsafeSet()
- return &set
+// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with
+// a specified capacity. Operations on the resulting set are not thread-safe.
+func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] {
+ s := newThreadUnsafeSetWithSize[T](cardinality)
+ return s
}
-func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
- a := NewThreadUnsafeSet()
- for _, item := range s {
- a.Add(item)
+// NewSetFromMapKeys creates and returns a new set with the given keys of the map.
+// Operations on the resulting set are thread-safe.
+func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
+ s := NewSetWithSize[T](len(val))
+
+ for k := range val {
+ s.Add(k)
}
- return a
+
+ return s
+}
+
+// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map.
+// Operations on the resulting set are not thread-safe.
+func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
+ s := NewThreadUnsafeSetWithSize[T](len(val))
+
+ for k := range val {
+ s.Add(k)
+ }
+
+ return s
}
diff --git a/set_test.go b/set_test.go
index 5931002..6017f45 100644
--- a/set_test.go
+++ b/set_test.go
@@ -2,7 +2,7 @@
Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -25,34 +25,59 @@ SOFTWARE.
package mapset
-import "testing"
+import (
+ "testing"
+)
-func makeSet(ints []int) Set {
- set := NewSet()
+func makeSetInt(ints []int) Set[int] {
+ s := NewSet[int]()
for _, i := range ints {
- set.Add(i)
+ s.Add(i)
}
- return set
+ return s
}
-func makeUnsafeSet(ints []int) Set {
- set := NewThreadUnsafeSet()
+func makeUnsafeSetInt(ints []int) Set[int] {
+ s := NewThreadUnsafeSet[int]()
for _, i := range ints {
- set.Add(i)
+ s.Add(i)
}
- return set
+ return s
}
-func Test_NewSet(t *testing.T) {
- a := NewSet()
+func makeSetIntWithAppend(ints ...int) Set[int] {
+ s := NewSet[int]()
+ s.Append(ints...)
+ return s
+}
+func makeUnsafeSetIntWithAppend(ints ...int) Set[int] {
+ s := NewThreadUnsafeSet[int]()
+ s.Append(ints...)
+ return s
+}
+
+func assertEqual[T comparable](a, b Set[T], t *testing.T) {
+ if !a.Equal(b) {
+ t.Errorf("%v != %v\n", a, b)
+ }
+}
+
+func Test_NewSet(t *testing.T) {
+ a := NewSet[int]()
if a.Cardinality() != 0 {
t.Error("NewSet should start out as an empty set")
}
+
+ assertEqual(NewSet([]int{}...), NewSet[int](), t)
+ assertEqual(NewSet([]int{1}...), NewSet(1), t)
+ assertEqual(NewSet([]int{1, 2}...), NewSet(1, 2), t)
+ assertEqual(NewSet([]string{"a"}...), NewSet("a"), t)
+ assertEqual(NewSet([]string{"a", "b"}...), NewSet("a", "b"), t)
}
func Test_NewUnsafeSet(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
if a.Cardinality() != 0 {
t.Error("NewSet should start out as an empty set")
@@ -60,7 +85,7 @@ func Test_NewUnsafeSet(t *testing.T) {
}
func Test_AddSet(t *testing.T) {
- a := makeSet([]int{1, 2, 3})
+ a := makeSetInt([]int{1, 2, 3})
if a.Cardinality() != 3 {
t.Error("AddSet does not have a size of 3 even though 3 items were added to a new set")
@@ -68,15 +93,31 @@ func Test_AddSet(t *testing.T) {
}
func Test_AddUnsafeSet(t *testing.T) {
- a := makeUnsafeSet([]int{1, 2, 3})
+ a := makeUnsafeSetInt([]int{1, 2, 3})
if a.Cardinality() != 3 {
t.Error("AddSet does not have a size of 3 even though 3 items were added to a new set")
}
}
+func Test_AppendSet(t *testing.T) {
+ a := makeSetIntWithAppend(1, 2, 3)
+
+ if a.Cardinality() != 3 {
+ t.Error("AppendSet does not have a size of 3 even though 3 items were added to a new set")
+ }
+}
+
+func Test_AppendUnsafeSet(t *testing.T) {
+ a := makeUnsafeSetIntWithAppend(1, 2, 3)
+
+ if a.Cardinality() != 3 {
+ t.Error("AppendSet does not have a size of 3 even though 3 items were added to a new set")
+ }
+}
+
func Test_AddSetNoDuplicate(t *testing.T) {
- a := makeSet([]int{7, 5, 3, 7})
+ a := makeSetInt([]int{7, 5, 3, 7})
if a.Cardinality() != 3 {
t.Error("AddSetNoDuplicate set should have 3 elements since 7 is a duplicate")
@@ -88,7 +129,7 @@ func Test_AddSetNoDuplicate(t *testing.T) {
}
func Test_AddUnsafeSetNoDuplicate(t *testing.T) {
- a := makeUnsafeSet([]int{7, 5, 3, 7})
+ a := makeUnsafeSetInt([]int{7, 5, 3, 7})
if a.Cardinality() != 3 {
t.Error("AddSetNoDuplicate set should have 3 elements since 7 is a duplicate")
@@ -99,8 +140,32 @@ func Test_AddUnsafeSetNoDuplicate(t *testing.T) {
}
}
+func Test_AppendSetNoDuplicate(t *testing.T) {
+ a := makeSetIntWithAppend(7, 5, 3, 7)
+
+ if a.Cardinality() != 3 {
+ t.Error("AppendSetNoDuplicate set should have 3 elements since 7 is a duplicate")
+ }
+
+ if !(a.Contains(7) && a.Contains(5) && a.Contains(3)) {
+ t.Error("AppendSetNoDuplicate set should have a 7, 5, and 3 in it.")
+ }
+}
+
+func Test_AppendUnsafeSetNoDuplicate(t *testing.T) {
+ a := makeUnsafeSetIntWithAppend(7, 5, 3, 7)
+
+ if a.Cardinality() != 3 {
+ t.Error("AppendSetNoDuplicate set should have 3 elements since 7 is a duplicate")
+ }
+
+ if !(a.Contains(7) && a.Contains(5) && a.Contains(3)) {
+ t.Error("AppendSetNoDuplicate set should have a 7, 5, and 3 in it.")
+ }
+}
+
func Test_RemoveSet(t *testing.T) {
- a := makeSet([]int{6, 3, 1})
+ a := makeSetInt([]int{6, 3, 1})
a.Remove(3)
@@ -120,8 +185,28 @@ func Test_RemoveSet(t *testing.T) {
}
}
+func Test_RemoveAllSet(t *testing.T) {
+ a := makeSetInt([]int{6, 3, 1, 8, 9})
+
+ a.RemoveAll(3, 1)
+
+ if a.Cardinality() != 3 {
+ t.Error("RemoveAll should only have 2 items in the set")
+ }
+
+ if !a.Contains(6, 8, 9) {
+ t.Error("RemoveAll should have only items (6,8,9) in the set")
+ }
+
+ a.RemoveAll(6, 8, 9)
+
+ if a.Cardinality() != 0 {
+ t.Error("RemoveSet should be an empty set after removing 6 and 1")
+ }
+}
+
func Test_RemoveUnsafeSet(t *testing.T) {
- a := makeUnsafeSet([]int{6, 3, 1})
+ a := makeUnsafeSetInt([]int{6, 3, 1})
a.Remove(3)
@@ -141,8 +226,28 @@ func Test_RemoveUnsafeSet(t *testing.T) {
}
}
+func Test_RemoveAllUnsafeSet(t *testing.T) {
+ a := makeUnsafeSetInt([]int{6, 3, 1, 8, 9})
+
+ a.RemoveAll(3, 1)
+
+ if a.Cardinality() != 3 {
+ t.Error("RemoveAll should only have 2 items in the set")
+ }
+
+ if !a.Contains(6, 8, 9) {
+ t.Error("RemoveAll should have only items (6,8,9) in the set")
+ }
+
+ a.RemoveAll(6, 8, 9)
+
+ if a.Cardinality() != 0 {
+ t.Error("RemoveSet should be an empty set after removing 6 and 1")
+ }
+}
+
func Test_ContainsSet(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
a.Add(71)
@@ -166,7 +271,7 @@ func Test_ContainsSet(t *testing.T) {
}
func Test_ContainsUnsafeSet(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
a.Add(71)
@@ -190,7 +295,7 @@ func Test_ContainsUnsafeSet(t *testing.T) {
}
func Test_ContainsMultipleSet(t *testing.T) {
- a := makeSet([]int{8, 6, 7, 5, 3, 0, 9})
+ a := makeSetInt([]int{8, 6, 7, 5, 3, 0, 9})
if !a.Contains(8, 6, 7, 5, 3, 0, 9) {
t.Error("ContainsAll should contain Jenny's phone number")
@@ -202,7 +307,7 @@ func Test_ContainsMultipleSet(t *testing.T) {
}
func Test_ContainsMultipleUnsafeSet(t *testing.T) {
- a := makeUnsafeSet([]int{8, 6, 7, 5, 3, 0, 9})
+ a := makeUnsafeSetInt([]int{8, 6, 7, 5, 3, 0, 9})
if !a.Contains(8, 6, 7, 5, 3, 0, 9) {
t.Error("ContainsAll should contain Jenny's phone number")
@@ -214,7 +319,7 @@ func Test_ContainsMultipleUnsafeSet(t *testing.T) {
}
func Test_ClearSet(t *testing.T) {
- a := makeSet([]int{2, 5, 9, 10})
+ a := makeSetInt([]int{2, 5, 9, 10})
a.Clear()
@@ -224,7 +329,7 @@ func Test_ClearSet(t *testing.T) {
}
func Test_ClearUnsafeSet(t *testing.T) {
- a := makeUnsafeSet([]int{2, 5, 9, 10})
+ a := makeUnsafeSetInt([]int{2, 5, 9, 10})
a.Clear()
@@ -234,7 +339,7 @@ func Test_ClearUnsafeSet(t *testing.T) {
}
func Test_CardinalitySet(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
if a.Cardinality() != 0 {
t.Error("set should be an empty set")
@@ -266,7 +371,7 @@ func Test_CardinalitySet(t *testing.T) {
}
func Test_CardinalityUnsafeSet(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
if a.Cardinality() != 0 {
t.Error("set should be an empty set")
@@ -298,9 +403,9 @@ func Test_CardinalityUnsafeSet(t *testing.T) {
}
func Test_SetIsSubset(t *testing.T) {
- a := makeSet([]int{1, 2, 3, 5, 7})
+ a := makeSetInt([]int{1, 2, 3, 5, 7})
- b := NewSet()
+ b := NewSet[int]()
b.Add(3)
b.Add(5)
b.Add(7)
@@ -316,10 +421,31 @@ func Test_SetIsSubset(t *testing.T) {
}
}
+func Test_SetIsProperSubset(t *testing.T) {
+ a := makeSetInt([]int{1, 2, 3, 5, 7})
+ b := makeSetInt([]int{7, 5, 3, 2, 1})
+
+ if !a.IsSubset(b) {
+ t.Error("set a should be a subset of set b")
+ }
+ if a.IsProperSubset(b) {
+ t.Error("set a should not be a proper subset of set b (they're equal)")
+ }
+
+ b.Add(72)
+
+ if !a.IsSubset(b) {
+ t.Error("set a should be a subset of set b")
+ }
+ if !a.IsProperSubset(b) {
+ t.Error("set a should be a proper subset of set b")
+ }
+}
+
func Test_UnsafeSetIsSubset(t *testing.T) {
- a := makeUnsafeSet([]int{1, 2, 3, 5, 7})
+ a := makeUnsafeSetInt([]int{1, 2, 3, 5, 7})
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[int]()
b.Add(3)
b.Add(5)
b.Add(7)
@@ -335,15 +461,41 @@ func Test_UnsafeSetIsSubset(t *testing.T) {
}
}
-func Test_SetIsSuperSet(t *testing.T) {
- a := NewSet()
+func Test_UnsafeSetIsProperSubset(t *testing.T) {
+ a := makeUnsafeSetInt([]int{1, 2, 3, 5, 7})
+ b := NewThreadUnsafeSet[int]()
+ b.Add(7)
+ b.Add(1)
+ b.Add(5)
+ b.Add(3)
+ b.Add(2)
+
+ if !a.IsSubset(b) {
+ t.Error("set a should be a subset of set b")
+ }
+ if a.IsProperSubset(b) {
+ t.Error("set a should not be a proper subset of set b (they're equal)")
+ }
+
+ b.Add(72)
+
+ if !a.IsSubset(b) {
+ t.Error("set a should be a subset of set b")
+ }
+ if !a.IsProperSubset(b) {
+ t.Error("set a should be a proper subset of set b because set b has 72")
+ }
+}
+
+func Test_SetIsSuperset(t *testing.T) {
+ a := NewSet[int]()
a.Add(9)
a.Add(5)
a.Add(2)
a.Add(1)
a.Add(11)
- b := NewSet()
+ b := NewSet[int]()
b.Add(5)
b.Add(2)
b.Add(11)
@@ -355,19 +507,56 @@ func Test_SetIsSuperSet(t *testing.T) {
b.Add(42)
if a.IsSuperset(b) {
- t.Error("set a should not be a superset of set b because set a has a 42")
+ t.Error("set a should not be a superset of set b because set b has a 42")
}
}
-func Test_UnsafeSetIsSuperSet(t *testing.T) {
- a := NewThreadUnsafeSet()
+func Test_SetIsProperSuperset(t *testing.T) {
+ a := NewSet[int]()
+ a.Add(5)
+ a.Add(2)
+ a.Add(11)
+
+ b := NewSet[int]()
+ b.Add(2)
+ b.Add(5)
+ b.Add(11)
+
+ if !a.IsSuperset(b) {
+ t.Error("set a should be a superset of set b")
+ }
+ if a.IsProperSuperset(b) {
+ t.Error("set a should not be a proper superset of set b (they're equal)")
+ }
+
+ a.Add(9)
+
+ if !a.IsSuperset(b) {
+ t.Error("set a should be a superset of set b")
+ }
+ if !a.IsProperSuperset(b) {
+ t.Error("set a not be a proper superset of set b because set a has a 9")
+ }
+
+ b.Add(42)
+
+ if a.IsSuperset(b) {
+ t.Error("set a should not be a superset of set b because set b has a 42")
+ }
+ if a.IsProperSuperset(b) {
+ t.Error("set a should not be a proper superset of set b because set b has a 42")
+ }
+}
+
+func Test_UnsafeSetIsSuperset(t *testing.T) {
+ a := NewThreadUnsafeSet[int]()
a.Add(9)
a.Add(5)
a.Add(2)
a.Add(1)
a.Add(11)
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[int]()
b.Add(5)
b.Add(2)
b.Add(11)
@@ -383,10 +572,47 @@ func Test_UnsafeSetIsSuperSet(t *testing.T) {
}
}
+func Test_UnsafeSetIsProperSuperset(t *testing.T) {
+ a := NewThreadUnsafeSet[int]()
+ a.Add(5)
+ a.Add(2)
+ a.Add(11)
+
+ b := NewThreadUnsafeSet[int]()
+ b.Add(2)
+ b.Add(5)
+ b.Add(11)
+
+ if !a.IsSuperset(b) {
+ t.Error("set a should be a superset of set b")
+ }
+ if a.IsProperSuperset(b) {
+ t.Error("set a should not be a proper superset of set b (they're equal)")
+ }
+
+ a.Add(9)
+
+ if !a.IsSuperset(b) {
+ t.Error("set a should be a superset of set b")
+ }
+ if !a.IsProperSuperset(b) {
+ t.Error("set a not be a proper superset of set b because set a has a 9")
+ }
+
+ b.Add(42)
+
+ if a.IsSuperset(b) {
+ t.Error("set a should not be a superset of set b because set b has a 42")
+ }
+ if a.IsProperSuperset(b) {
+ t.Error("set a should not be a proper superset of set b because set b has a 42")
+ }
+}
+
func Test_SetUnion(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
- b := NewSet()
+ b := NewSet[int]()
b.Add(1)
b.Add(2)
b.Add(3)
@@ -399,7 +625,7 @@ func Test_SetUnion(t *testing.T) {
t.Error("set c is unioned with an empty set and therefore should have 5 elements in it")
}
- d := NewSet()
+ d := NewSet[int]()
d.Add(10)
d.Add(14)
d.Add(0)
@@ -409,20 +635,20 @@ func Test_SetUnion(t *testing.T) {
t.Error("set e should should have 8 elements in it after being unioned with set c to d")
}
- f := NewSet()
+ f := NewSet[int]()
f.Add(14)
f.Add(3)
g := f.Union(e)
if g.Cardinality() != 8 {
- t.Error("set g should still ahve 8 elements in it after being unioned with set f that has duplicates")
+ t.Error("set g should still have 8 elements in it after being unioned with set f that has duplicates")
}
}
func Test_UnsafeSetUnion(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[int]()
b.Add(1)
b.Add(2)
b.Add(3)
@@ -435,7 +661,7 @@ func Test_UnsafeSetUnion(t *testing.T) {
t.Error("set c is unioned with an empty set and therefore should have 5 elements in it")
}
- d := NewThreadUnsafeSet()
+ d := NewThreadUnsafeSet[int]()
d.Add(10)
d.Add(14)
d.Add(0)
@@ -445,23 +671,23 @@ func Test_UnsafeSetUnion(t *testing.T) {
t.Error("set e should should have 8 elements in it after being unioned with set c to d")
}
- f := NewThreadUnsafeSet()
+ f := NewThreadUnsafeSet[int]()
f.Add(14)
f.Add(3)
g := f.Union(e)
if g.Cardinality() != 8 {
- t.Error("set g should still ahve 8 elements in it after being unioned with set f that has duplicates")
+ t.Error("set g should still have 8 elements in it after being unioned with set f that has duplicates")
}
}
func Test_SetIntersect(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
a.Add(1)
a.Add(3)
a.Add(5)
- b := NewSet()
+ b := NewSet[int]()
a.Add(2)
a.Add(4)
a.Add(6)
@@ -483,12 +709,12 @@ func Test_SetIntersect(t *testing.T) {
}
func Test_UnsafeSetIntersect(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
a.Add(1)
a.Add(3)
a.Add(5)
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[int]()
a.Add(2)
a.Add(4)
a.Add(6)
@@ -510,12 +736,12 @@ func Test_UnsafeSetIntersect(t *testing.T) {
}
func Test_SetDifference(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
a.Add(1)
a.Add(2)
a.Add(3)
- b := NewSet()
+ b := NewSet[int]()
b.Add(1)
b.Add(3)
b.Add(4)
@@ -531,12 +757,12 @@ func Test_SetDifference(t *testing.T) {
}
func Test_UnsafeSetDifference(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
a.Add(1)
a.Add(2)
a.Add(3)
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[int]()
b.Add(1)
b.Add(3)
b.Add(4)
@@ -552,13 +778,13 @@ func Test_UnsafeSetDifference(t *testing.T) {
}
func Test_SetSymmetricDifference(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
a.Add(1)
a.Add(2)
a.Add(3)
a.Add(45)
- b := NewSet()
+ b := NewSet[int]()
b.Add(1)
b.Add(3)
b.Add(4)
@@ -574,13 +800,13 @@ func Test_SetSymmetricDifference(t *testing.T) {
}
func Test_UnsafeSetSymmetricDifference(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
a.Add(1)
a.Add(2)
a.Add(3)
a.Add(45)
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[int]()
b.Add(1)
b.Add(3)
b.Add(4)
@@ -596,8 +822,8 @@ func Test_UnsafeSetSymmetricDifference(t *testing.T) {
}
func Test_SetEqual(t *testing.T) {
- a := NewSet()
- b := NewSet()
+ a := NewSet[int]()
+ b := NewSet[int]()
if !a.Equal(b) {
t.Error("Both a and b are empty sets, and should be equal")
@@ -633,8 +859,8 @@ func Test_SetEqual(t *testing.T) {
}
func Test_UnsafeSetEqual(t *testing.T) {
- a := NewThreadUnsafeSet()
- b := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
+ b := NewThreadUnsafeSet[int]()
if !a.Equal(b) {
t.Error("Both a and b are empty sets, and should be equal")
@@ -670,7 +896,7 @@ func Test_UnsafeSetEqual(t *testing.T) {
}
func Test_SetClone(t *testing.T) {
- a := NewSet()
+ a := NewSet[int]()
a.Add(1)
a.Add(2)
@@ -694,7 +920,7 @@ func Test_SetClone(t *testing.T) {
}
func Test_UnsafeSetClone(t *testing.T) {
- a := NewThreadUnsafeSet()
+ a := NewThreadUnsafeSet[int]()
a.Add(1)
a.Add(2)
@@ -717,71 +943,211 @@ func Test_UnsafeSetClone(t *testing.T) {
}
}
-func Test_Iterator(t *testing.T) {
- a := NewSet()
+func Test_Each(t *testing.T) {
+ a := NewSet[string]()
+
+ a.Add("Z")
+ a.Add("Y")
+ a.Add("X")
+ a.Add("W")
+
+ b := NewSet[string]()
+ a.Each(func(elem string) bool {
+ b.Add(elem)
+ return false
+ })
+
+ if !a.Equal(b) {
+ t.Error("The sets are not equal after iterating (Each) through the first set")
+ }
+
+ var count int
+ a.Each(func(elem string) bool {
+ if count == 2 {
+ return true
+ }
+ count++
+ return false
+ })
+ if count != 2 {
+ t.Error("Iteration should stop on the way")
+ }
+}
+
+func Test_Iter(t *testing.T) {
+ a := NewSet[string]()
a.Add("Z")
a.Add("Y")
a.Add("X")
a.Add("W")
- b := NewSet()
+ b := NewSet[string]()
for val := range a.Iter() {
b.Add(val)
}
if !a.Equal(b) {
- t.Error("The sets are not equal after iterating through the first set")
+ t.Error("The sets are not equal after iterating (Iter) through the first set")
}
}
-func Test_UnsafeIterator(t *testing.T) {
- a := NewThreadUnsafeSet()
+func Test_UnsafeIter(t *testing.T) {
+ a := NewThreadUnsafeSet[string]()
a.Add("Z")
a.Add("Y")
a.Add("X")
a.Add("W")
- b := NewThreadUnsafeSet()
+ b := NewThreadUnsafeSet[string]()
for val := range a.Iter() {
b.Add(val)
}
if !a.Equal(b) {
- t.Error("The sets are not equal after iterating through the first set")
+ t.Error("The sets are not equal after iterating (Iter) through the first set")
}
}
-func Test_PowerSet(t *testing.T) {
- a := NewThreadUnsafeSet()
+func Test_Iterator(t *testing.T) {
+ a := NewSet[string]()
- a.Add(1)
- a.Add("delta")
- a.Add("chi")
- a.Add(4)
+ a.Add("Z")
+ a.Add("Y")
+ a.Add("X")
+ a.Add("W")
+
+ b := NewSet[string]()
+ for val := range a.Iterator().C {
+ b.Add(val)
+ }
+
+ if !a.Equal(b) {
+ t.Error("The sets are not equal after iterating (Iterator) through the first set")
+ }
+}
+
+func Test_UnsafeIterator(t *testing.T) {
+ a := NewThreadUnsafeSet[string]()
+
+ a.Add("Z")
+ a.Add("Y")
+ a.Add("X")
+ a.Add("W")
+
+ b := NewThreadUnsafeSet[string]()
+ for val := range a.Iterator().C {
+ b.Add(val)
+ }
+
+ if !a.Equal(b) {
+ t.Error("The sets are not equal after iterating (Iterator) through the first set")
+ }
+}
- b := a.PowerSet()
- if b.Cardinality() != 16 {
- t.Error("unexpected PowerSet cardinality")
+func Test_IteratorStop(t *testing.T) {
+ a := NewSet[string]()
+
+ a.Add("Z")
+ a.Add("Y")
+ a.Add("X")
+ a.Add("W")
+
+ it := a.Iterator()
+ it.Stop()
+ for range it.C {
+ t.Error("The iterating (Iterator) did not stop after Stop() has been called")
+ }
+}
+
+func Test_PopSafe(t *testing.T) {
+ a := NewSet[string]()
+
+ a.Add("a")
+ a.Add("b")
+ a.Add("c")
+ a.Add("d")
+
+ aPop := func() (v string) {
+ v, _ = a.Pop()
+ return
+ }
+
+ captureSet := NewSet[string]()
+ captureSet.Add(aPop())
+ captureSet.Add(aPop())
+ captureSet.Add(aPop())
+ captureSet.Add(aPop())
+ finalNil := aPop()
+
+ if captureSet.Cardinality() != 4 {
+ t.Error("unexpected captureSet cardinality; should be 4")
+ }
+
+ if a.Cardinality() != 0 {
+ t.Error("unepxected a cardinality; should be zero")
+ }
+
+ if !captureSet.Contains("c", "a", "d", "b") {
+ t.Error("unexpected result set; should be a,b,c,d (any order is fine")
+ }
+
+ if finalNil != "" {
+ t.Error("when original set is empty, further pops should result in nil")
+ }
+}
+
+func Test_PopUnsafe(t *testing.T) {
+ a := NewThreadUnsafeSet[string]()
+
+ a.Add("a")
+ a.Add("b")
+ a.Add("c")
+ a.Add("d")
+
+ aPop := func() (v string) {
+ v, _ = a.Pop()
+ return
+ }
+
+ captureSet := NewThreadUnsafeSet[string]()
+ captureSet.Add(aPop())
+ captureSet.Add(aPop())
+ captureSet.Add(aPop())
+ captureSet.Add(aPop())
+ finalNil := aPop()
+
+ if captureSet.Cardinality() != 4 {
+ t.Error("unexpected captureSet cardinality; should be 4")
+ }
+
+ if a.Cardinality() != 0 {
+ t.Error("unepxected a cardinality; should be zero")
+ }
+
+ if !captureSet.Contains("c", "a", "d", "b") {
+ t.Error("unexpected result set; should be a,b,c,d (any order is fine")
+ }
+
+ if finalNil != "" {
+ t.Error("when original set is empty, further pops should result in nil")
}
}
func Test_EmptySetProperties(t *testing.T) {
- empty := NewSet()
+ empty := NewSet[string]()
- a := NewSet()
- a.Add(1)
+ a := NewSet[string]()
+ a.Add("1")
a.Add("foo")
a.Add("bar")
- b := NewSet()
+ b := NewSet[string]()
b.Add("one")
b.Add("two")
- b.Add(3)
- b.Add(4)
-
- c := NewSet()
+ b.Add("3")
+ b.Add("4")
if !empty.IsSubset(a) || !empty.IsSubset(b) {
t.Error("The empty set is supposed to be a subset of all sets")
@@ -795,7 +1161,7 @@ func Test_EmptySetProperties(t *testing.T) {
t.Error("The empty set is supposed to be a subset and a superset of itself")
}
- c = a.Union(empty)
+ c := a.Union(empty)
if !c.Equal(a) {
t.Error("The union of any set with the empty set is supposed to be equal to itself")
}
@@ -805,65 +1171,97 @@ func Test_EmptySetProperties(t *testing.T) {
t.Error("The intesection of any set with the empty set is supposed to be the empty set")
}
- c = a.CartesianProduct(empty)
- if c.Cardinality() != 0 {
- t.Error("Cartesian product of any set and the empty set must be the empty set")
- }
-
if empty.Cardinality() != 0 {
t.Error("Cardinality of the empty set is supposed to be zero")
}
+}
- c = empty.PowerSet()
- if c.Cardinality() != 1 {
- t.Error("Cardinality of the power set of the empty set is supposed to be one { {} }")
+func Test_ToSliceUnthreadsafe(t *testing.T) {
+ s := makeUnsafeSetInt([]int{1, 2, 3})
+ setAsSlice := s.ToSlice()
+ if len(setAsSlice) != s.Cardinality() {
+ t.Errorf("Set length is incorrect: %v", len(setAsSlice))
+ }
+
+ for _, i := range setAsSlice {
+ if !s.Contains(i) {
+ t.Errorf("Set is missing element: %v", i)
+ }
}
}
-func Test_CartesianProduct(t *testing.T) {
- a := NewThreadUnsafeSet()
- b := NewThreadUnsafeSet()
- empty := NewThreadUnsafeSet()
+func Test_NewSetFromMapKey_Ints(t *testing.T) {
+ m := map[int]int{
+ 5: 5,
+ 2: 3,
+ }
- a.Add(1)
- a.Add(2)
- a.Add(3)
+ s := NewSetFromMapKeys(m)
- b.Add("one")
- b.Add("two")
- b.Add("three")
- b.Add("alpha")
- b.Add("gamma")
+ if len(m) != s.Cardinality() {
+ t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality())
+ }
- c := a.CartesianProduct(b)
- d := b.CartesianProduct(a)
+ for k := range m {
+ if !s.Contains(k) {
+ t.Errorf("Element %d not found in map: %v", k, m)
+ }
+ }
+}
- if c.Cardinality() != d.Cardinality() {
- t.Error("Cardinality of AxB must be equal to BxA")
+func Test_NewSetFromMapKey_Strings(t *testing.T) {
+ m := map[int]int{
+ 5: 5,
+ 2: 3,
}
- if c.Cardinality() != (a.Cardinality() * b.Cardinality()) {
- t.Error("Unexpected cardinality for cartesian product set")
+ s := NewSetFromMapKeys(m)
+
+ if len(m) != s.Cardinality() {
+ t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality())
}
- c = a.CartesianProduct(empty)
- d = empty.CartesianProduct(b)
+ for k := range m {
+ if !s.Contains(k) {
+ t.Errorf("Element %q not found in map: %v", k, m)
+ }
+ }
+}
- if c.Cardinality() != 0 || d.Cardinality() != 0 {
- t.Error("Cartesian product of any set and the emtpy set Ax0 || 0xA must be the empty set")
+func Test_NewThreadUnsafeSetFromMapKey_Ints(t *testing.T) {
+ m := map[int]int{
+ 5: 5,
+ 2: 3,
+ }
+
+ s := NewThreadUnsafeSetFromMapKeys(m)
+
+ if len(m) != s.Cardinality() {
+ t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality())
+ }
+
+ for k := range m {
+ if !s.Contains(k) {
+ t.Errorf("Element %d not found in map: %v", k, m)
+ }
}
}
-func Test_ToSliceUnthreadsafe(t *testing.T) {
- s := makeUnsafeSet([]int{1, 2, 3})
- setAsSlice := s.ToSlice()
- if len(setAsSlice) != s.Cardinality() {
- t.Errorf("Set length is incorrect: %v", len(setAsSlice))
+func Test_NewThreadUnsafeSetFromMapKey_Strings(t *testing.T) {
+ m := map[int]int{
+ 5: 5,
+ 2: 3,
}
- for _, i := range setAsSlice {
- if !s.Contains(i) {
- t.Errorf("Set is missing element: %v", i)
+ s := NewThreadUnsafeSetFromMapKeys(m)
+
+ if len(m) != s.Cardinality() {
+ t.Errorf("Length of Set is not the same as the map. Expected: %d. Actual: %d", len(m), s.Cardinality())
+ }
+
+ for k := range m {
+ if !s.Contains(k) {
+ t.Errorf("Element %q not found in map: %v", k, m)
}
}
}
@@ -877,7 +1275,7 @@ func Test_Example(t *testing.T) {
requiredClasses.Add("Biology")
scienceSlice := []interface{}{"Biology", "Chemistry"}
- scienceClasses := NewSetFromSlice(scienceSlice)
+ scienceClasses := NewSet(scienceSlice)
electiveClasses := NewSet()
electiveClasses.Add("Welding")
diff --git a/threadsafe.go b/threadsafe.go
index 9dca94a..9e3a0ca 100644
--- a/threadsafe.go
+++ b/threadsafe.go
@@ -2,7 +2,7 @@
Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -27,178 +27,253 @@ package mapset
import "sync"
-type threadSafeSet struct {
- s threadUnsafeSet
+type threadSafeSet[T comparable] struct {
sync.RWMutex
+ uss threadUnsafeSet[T]
}
-func newThreadSafeSet() threadSafeSet {
- return threadSafeSet{s: newThreadUnsafeSet()}
+func newThreadSafeSet[T comparable]() *threadSafeSet[T] {
+ return &threadSafeSet[T]{
+ uss: newThreadUnsafeSet[T](),
+ }
+}
+
+func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] {
+ return &threadSafeSet[T]{
+ uss: newThreadUnsafeSetWithSize[T](cardinality),
+ }
+}
+
+func (t *threadSafeSet[T]) Add(v T) bool {
+ t.Lock()
+ ret := t.uss.Add(v)
+ t.Unlock()
+ return ret
}
-func (set *threadSafeSet) Add(i interface{}) bool {
- set.Lock()
- ret := set.s.Add(i)
- set.Unlock()
+func (t *threadSafeSet[T]) Append(v ...T) int {
+ t.Lock()
+ ret := t.uss.Append(v...)
+ t.Unlock()
return ret
}
-func (set *threadSafeSet) Contains(i ...interface{}) bool {
- set.RLock()
- ret := set.s.Contains(i...)
- set.RUnlock()
+func (t *threadSafeSet[T]) Contains(v ...T) bool {
+ t.RLock()
+ ret := t.uss.Contains(v...)
+ t.RUnlock()
+
return ret
}
-func (set *threadSafeSet) IsSubset(other Set) bool {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool {
+ o := other.(*threadSafeSet[T])
- set.RLock()
+ t.RLock()
o.RLock()
- ret := set.s.IsSubset(&o.s)
- set.RUnlock()
+ ret := t.uss.IsSubset(o.uss)
+ t.RUnlock()
o.RUnlock()
return ret
}
-func (set *threadSafeSet) IsSuperset(other Set) bool {
- return other.IsSubset(set)
+func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool {
+ o := other.(*threadSafeSet[T])
+
+ t.RLock()
+ defer t.RUnlock()
+ o.RLock()
+ defer o.RUnlock()
+
+ return t.uss.IsProperSubset(o.uss)
}
-func (set *threadSafeSet) Union(other Set) Set {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool {
+ return other.IsSubset(t)
+}
- set.RLock()
+func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool {
+ return other.IsProperSubset(t)
+}
+
+func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] {
+ o := other.(*threadSafeSet[T])
+
+ t.RLock()
o.RLock()
- unsafeUnion := set.s.Union(&o.s).(*threadUnsafeSet)
- ret := &threadSafeSet{s: *unsafeUnion}
- set.RUnlock()
+ unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T])
+ ret := &threadSafeSet[T]{uss: unsafeUnion}
+ t.RUnlock()
o.RUnlock()
return ret
}
-func (set *threadSafeSet) Intersect(other Set) Set {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] {
+ o := other.(*threadSafeSet[T])
- set.RLock()
+ t.RLock()
o.RLock()
- unsafeIntersection := set.s.Intersect(&o.s).(*threadUnsafeSet)
- ret := &threadSafeSet{s: *unsafeIntersection}
- set.RUnlock()
+ unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T])
+ ret := &threadSafeSet[T]{uss: unsafeIntersection}
+ t.RUnlock()
o.RUnlock()
return ret
}
-func (set *threadSafeSet) Difference(other Set) Set {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] {
+ o := other.(*threadSafeSet[T])
- set.RLock()
+ t.RLock()
o.RLock()
- unsafeDifference := set.s.Difference(&o.s).(*threadUnsafeSet)
- ret := &threadSafeSet{s: *unsafeDifference}
- set.RUnlock()
+ unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T])
+ ret := &threadSafeSet[T]{uss: unsafeDifference}
+ t.RUnlock()
o.RUnlock()
return ret
}
-func (set *threadSafeSet) SymmetricDifference(other Set) Set {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] {
+ o := other.(*threadSafeSet[T])
- unsafeDifference := set.s.SymmetricDifference(&o.s).(*threadUnsafeSet)
- return &threadSafeSet{s: *unsafeDifference}
+ t.RLock()
+ o.RLock()
+
+ unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T])
+ ret := &threadSafeSet[T]{uss: unsafeDifference}
+ t.RUnlock()
+ o.RUnlock()
+ return ret
+}
+
+func (t *threadSafeSet[T]) Clear() {
+ t.Lock()
+ t.uss.Clear()
+ t.Unlock()
}
-func (set *threadSafeSet) Clear() {
- set.Lock()
- set.s = newThreadUnsafeSet()
- set.Unlock()
+func (t *threadSafeSet[T]) Remove(v T) {
+ t.Lock()
+ delete(t.uss, v)
+ t.Unlock()
}
-func (set *threadSafeSet) Remove(i interface{}) {
- set.Lock()
- delete(set.s, i)
- set.Unlock()
+func (t *threadSafeSet[T]) RemoveAll(i ...T) {
+ t.Lock()
+ t.uss.RemoveAll(i...)
+ t.Unlock()
}
-func (set *threadSafeSet) Cardinality() int {
- set.RLock()
- defer set.RUnlock()
- return len(set.s)
+func (t *threadSafeSet[T]) Cardinality() int {
+ t.RLock()
+ defer t.RUnlock()
+ return len(t.uss)
}
-func (set *threadSafeSet) Iter() <-chan interface{} {
- ch := make(chan interface{})
+func (t *threadSafeSet[T]) Each(cb func(T) bool) {
+ t.RLock()
+ for elem := range t.uss {
+ if cb(elem) {
+ break
+ }
+ }
+ t.RUnlock()
+}
+
+func (t *threadSafeSet[T]) Iter() <-chan T {
+ ch := make(chan T)
go func() {
- set.RLock()
+ t.RLock()
- for elem := range set.s {
+ for elem := range t.uss {
ch <- elem
}
close(ch)
- set.RUnlock()
+ t.RUnlock()
}()
return ch
}
-func (set *threadSafeSet) Equal(other Set) bool {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) Iterator() *Iterator[T] {
+ iterator, ch, stopCh := newIterator[T]()
- set.RLock()
+ go func() {
+ t.RLock()
+ L:
+ for elem := range t.uss {
+ select {
+ case <-stopCh:
+ break L
+ case ch <- elem:
+ }
+ }
+ close(ch)
+ t.RUnlock()
+ }()
+
+ return iterator
+}
+
+func (t *threadSafeSet[T]) Equal(other Set[T]) bool {
+ o := other.(*threadSafeSet[T])
+
+ t.RLock()
o.RLock()
- ret := set.s.Equal(&o.s)
- set.RUnlock()
+ ret := t.uss.Equal(o.uss)
+ t.RUnlock()
o.RUnlock()
return ret
}
-func (set *threadSafeSet) Clone() Set {
- set.RLock()
+func (t *threadSafeSet[T]) Clone() Set[T] {
+ t.RLock()
- unsafeClone := set.s.Clone().(*threadUnsafeSet)
- ret := &threadSafeSet{s: *unsafeClone}
- set.RUnlock()
+ unsafeClone := t.uss.Clone().(threadUnsafeSet[T])
+ ret := &threadSafeSet[T]{uss: unsafeClone}
+ t.RUnlock()
return ret
}
-func (set *threadSafeSet) String() string {
- set.RLock()
- ret := set.s.String()
- set.RUnlock()
+func (t *threadSafeSet[T]) String() string {
+ t.RLock()
+ ret := t.uss.String()
+ t.RUnlock()
return ret
}
-func (set *threadSafeSet) PowerSet() Set {
- set.RLock()
- ret := set.s.PowerSet()
- set.RUnlock()
- return ret
+func (t *threadSafeSet[T]) Pop() (T, bool) {
+ t.Lock()
+ defer t.Unlock()
+ return t.uss.Pop()
}
-func (set *threadSafeSet) CartesianProduct(other Set) Set {
- o := other.(*threadSafeSet)
+func (t *threadSafeSet[T]) ToSlice() []T {
+ keys := make([]T, 0, t.Cardinality())
+ t.RLock()
+ for elem := range t.uss {
+ keys = append(keys, elem)
+ }
+ t.RUnlock()
+ return keys
+}
- set.RLock()
- o.RLock()
+func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) {
+ t.RLock()
+ b, err := t.uss.MarshalJSON()
+ t.RUnlock()
- unsafeCartProduct := set.s.CartesianProduct(&o.s).(*threadUnsafeSet)
- ret := &threadSafeSet{s: *unsafeCartProduct}
- set.RUnlock()
- o.RUnlock()
- return ret
+ return b, err
}
-func (set *threadSafeSet) ToSlice() []interface{} {
- set.RLock()
- keys := make([]interface{}, 0, set.Cardinality())
- for elem := range set.s {
- keys = append(keys, elem)
- }
- set.RUnlock()
- return keys
+func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error {
+ t.RLock()
+ err := t.uss.UnmarshalJSON(p)
+ t.RUnlock()
+
+ return err
}
diff --git a/threadsafe_test.go b/threadsafe_test.go
index d207660..a74df68 100644
--- a/threadsafe_test.go
+++ b/threadsafe_test.go
@@ -2,7 +2,7 @@
Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -26,9 +26,11 @@ SOFTWARE.
package mapset
import (
+ "encoding/json"
"math/rand"
"runtime"
"sync"
+ "sync/atomic"
"testing"
)
@@ -37,7 +39,7 @@ const N = 1000
func Test_AddConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
var wg sync.WaitGroup
@@ -57,10 +59,34 @@ func Test_AddConcurrent(t *testing.T) {
}
}
+func Test_AppendConcurrent(t *testing.T) {
+ runtime.GOMAXPROCS(2)
+
+ s := NewSet[int]()
+ ints := rand.Perm(N)
+
+ n := len(ints) >> 1
+ var wg sync.WaitGroup
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ go func(i int) {
+ s.Append(i, N-i-1)
+ wg.Done()
+ }(i)
+ }
+
+ wg.Wait()
+ for _, i := range ints {
+ if !s.Contains(i) {
+ t.Errorf("Set is missing element: %v", i)
+ }
+ }
+}
+
func Test_CardinalityConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
var wg sync.WaitGroup
wg.Add(1)
@@ -84,7 +110,7 @@ func Test_CardinalityConcurrent(t *testing.T) {
func Test_ClearConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
var wg sync.WaitGroup
@@ -105,7 +131,7 @@ func Test_ClearConcurrent(t *testing.T) {
func Test_CloneConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
for _, v := range ints {
@@ -120,25 +146,27 @@ func Test_CloneConcurrent(t *testing.T) {
wg.Done()
}(i)
}
-
s.Clone()
+ wg.Wait()
}
func Test_ContainsConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
+ integers := make([]int, 0)
for _, v := range ints {
s.Add(v)
- interfaces = append(interfaces, v)
+ integers = append(integers, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
- s.Contains(interfaces...)
+ s.Contains(integers...)
+ wg.Done()
}()
}
wg.Wait()
@@ -147,19 +175,19 @@ func Test_ContainsConcurrent(t *testing.T) {
func Test_DifferenceConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s, ss := NewSet(), NewSet()
+ s, ss := NewSet[int](), NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
for _, v := range ints {
s.Add(v)
ss.Add(v)
- interfaces = append(interfaces, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
s.Difference(ss)
+ wg.Done()
}()
}
wg.Wait()
@@ -168,19 +196,19 @@ func Test_DifferenceConcurrent(t *testing.T) {
func Test_EqualConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s, ss := NewSet(), NewSet()
+ s, ss := NewSet[int](), NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
for _, v := range ints {
s.Add(v)
ss.Add(v)
- interfaces = append(interfaces, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
s.Equal(ss)
+ wg.Done()
}()
}
wg.Wait()
@@ -189,19 +217,19 @@ func Test_EqualConcurrent(t *testing.T) {
func Test_IntersectConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s, ss := NewSet(), NewSet()
+ s, ss := NewSet[int](), NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
for _, v := range ints {
s.Add(v)
ss.Add(v)
- interfaces = append(interfaces, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
s.Intersect(ss)
+ wg.Done()
}()
}
wg.Wait()
@@ -210,19 +238,40 @@ func Test_IntersectConcurrent(t *testing.T) {
func Test_IsSubsetConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s, ss := NewSet(), NewSet()
+ s, ss := NewSet[int](), NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
for _, v := range ints {
s.Add(v)
ss.Add(v)
- interfaces = append(interfaces, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
s.IsSubset(ss)
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func Test_IsProperSubsetConcurrent(t *testing.T) {
+ runtime.GOMAXPROCS(2)
+
+ s, ss := NewSet[int](), NewSet[int]()
+ ints := rand.Perm(N)
+ for _, v := range ints {
+ s.Add(v)
+ ss.Add(v)
+ }
+
+ var wg sync.WaitGroup
+ for range ints {
+ wg.Add(1)
+ go func() {
+ s.IsProperSubset(ss)
+ wg.Done()
}()
}
wg.Wait()
@@ -231,35 +280,85 @@ func Test_IsSubsetConcurrent(t *testing.T) {
func Test_IsSupersetConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s, ss := NewSet(), NewSet()
+ s, ss := NewSet[int](), NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
for _, v := range ints {
s.Add(v)
ss.Add(v)
- interfaces = append(interfaces, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
s.IsSuperset(ss)
+ wg.Done()
}()
}
wg.Wait()
}
+func Test_IsProperSupersetConcurrent(t *testing.T) {
+ runtime.GOMAXPROCS(2)
+
+ s, ss := NewSet[int](), NewSet[int]()
+ ints := rand.Perm(N)
+ for _, v := range ints {
+ s.Add(v)
+ ss.Add(v)
+ }
+
+ var wg sync.WaitGroup
+ for range ints {
+ wg.Add(1)
+ go func() {
+ s.IsProperSuperset(ss)
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func Test_EachConcurrent(t *testing.T) {
+ runtime.GOMAXPROCS(2)
+ concurrent := 10
+
+ s := NewSet[int]()
+ ints := rand.Perm(N)
+ for _, v := range ints {
+ s.Add(v)
+ }
+
+ var count int64
+ wg := new(sync.WaitGroup)
+ wg.Add(concurrent)
+ for n := 0; n < concurrent; n++ {
+ go func() {
+ defer wg.Done()
+ s.Each(func(elem int) bool {
+ atomic.AddInt64(&count, 1)
+ return false
+ })
+ }()
+ }
+ wg.Wait()
+
+ if count != int64(N*concurrent) {
+ t.Errorf("%v != %v", count, int64(N*concurrent))
+ }
+}
+
func Test_IterConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
for _, v := range ints {
s.Add(v)
}
- cs := make([]<-chan interface{}, 0)
- for _ = range ints {
+ cs := make([]<-chan int, 0)
+ for range ints {
cs = append(cs, s.Iter())
}
@@ -278,14 +377,14 @@ func Test_IterConcurrent(t *testing.T) {
close(c)
}()
- for _ = range c {
+ for range c {
}
}
func Test_RemoveConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
for _, v := range ints {
s.Add(v)
@@ -309,7 +408,7 @@ func Test_RemoveConcurrent(t *testing.T) {
func Test_StringConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
for _, v := range ints {
s.Add(v)
@@ -317,9 +416,9 @@ func Test_StringConcurrent(t *testing.T) {
var wg sync.WaitGroup
wg.Add(len(ints))
- for _ = range ints {
+ for range ints {
go func() {
- s.String()
+ _ = s.String()
wg.Done()
}()
}
@@ -329,19 +428,19 @@ func Test_StringConcurrent(t *testing.T) {
func Test_SymmetricDifferenceConcurrent(t *testing.T) {
runtime.GOMAXPROCS(2)
- s, ss := NewSet(), NewSet()
+ s, ss := NewSet[int](), NewSet[int]()
ints := rand.Perm(N)
- interfaces := make([]interface{}, 0)
for _, v := range ints {
s.Add(v)
ss.Add(v)
- interfaces = append(interfaces, v)
}
var wg sync.WaitGroup
- for _ = range ints {
+ for range ints {
+ wg.Add(1)
go func() {
s.SymmetricDifference(ss)
+ wg.Done()
}()
}
wg.Wait()
@@ -350,7 +449,7 @@ func Test_SymmetricDifferenceConcurrent(t *testing.T) {
func Test_ToSlice(t *testing.T) {
runtime.GOMAXPROCS(2)
- s := NewSet()
+ s := NewSet[int]()
ints := rand.Perm(N)
var wg sync.WaitGroup
@@ -374,3 +473,77 @@ func Test_ToSlice(t *testing.T) {
}
}
}
+
+// Test_ToSliceDeadlock - fixes issue: https://github.com/deckarep/golang-set/issues/36
+// This code reveals the deadlock however it doesn't happen consistently.
+func Test_ToSliceDeadlock(t *testing.T) {
+ runtime.GOMAXPROCS(2)
+
+ var wg sync.WaitGroup
+ set := NewSet[int]()
+ workers := 10
+ wg.Add(workers)
+ for i := 1; i <= workers; i++ {
+ go func() {
+ for j := 0; j < 1000; j++ {
+ set.Add(1)
+ set.ToSlice()
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func Test_UnmarshalJSON(t *testing.T) {
+ s := []byte(`["test", "1", "2", "3"]`) //,["4,5,6"]]`)
+ expected := NewSet(
+ []string{
+ string(json.Number("1")),
+ string(json.Number("2")),
+ string(json.Number("3")),
+ "test",
+ }...,
+ )
+
+ actual := NewSet[string]()
+ err := json.Unmarshal(s, actual)
+ if err != nil {
+ t.Errorf("Error should be nil: %v", err)
+ }
+
+ if !expected.Equal(actual) {
+ t.Errorf("Expected no difference, got: %v", expected.Difference(actual))
+ }
+}
+
+func Test_MarshalJSON(t *testing.T) {
+ expected := NewSet(
+ []string{
+ string(json.Number("1")),
+ "test",
+ }...,
+ )
+
+ b, err := json.Marshal(
+ NewSet(
+ []string{
+ "1",
+ "test",
+ }...,
+ ),
+ )
+ if err != nil {
+ t.Errorf("Error should be nil: %v", err)
+ }
+
+ actual := NewSet[string]()
+ err = json.Unmarshal(b, actual)
+ if err != nil {
+ t.Errorf("Error should be nil: %v", err)
+ }
+
+ if !expected.Equal(actual) {
+ t.Errorf("Expected no difference, got: %v", expected.Difference(actual))
+ }
+}
diff --git a/threadunsafe.go b/threadunsafe.go
index 124521e..e5f4629 100644
--- a/threadunsafe.go
+++ b/threadunsafe.go
@@ -2,7 +2,7 @@
Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -26,131 +26,164 @@ SOFTWARE.
package mapset
import (
+ "bytes"
+ "encoding/json"
"fmt"
- "reflect"
"strings"
)
-type threadUnsafeSet map[interface{}]struct{}
+type threadUnsafeSet[T comparable] map[T]struct{}
-type orderedPair struct {
- first interface{}
- second interface{}
+// Assert concrete type:threadUnsafeSet adheres to Set interface.
+var _ Set[string] = (threadUnsafeSet[string])(nil)
+
+func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] {
+ return make(threadUnsafeSet[T])
+}
+
+func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] {
+ return make(threadUnsafeSet[T], cardinality)
}
-func newThreadUnsafeSet() threadUnsafeSet {
- return make(threadUnsafeSet)
+func (s threadUnsafeSet[T]) Add(v T) bool {
+ prevLen := len(s)
+ s[v] = struct{}{}
+ return prevLen != len(s)
}
-func (pair *orderedPair) Equal(other orderedPair) bool {
- if pair.first == other.first &&
- pair.second == other.second {
- return true
+func (s threadUnsafeSet[T]) Append(v ...T) int {
+ prevLen := len(s)
+ for _, val := range v {
+ (s)[val] = struct{}{}
}
+ return len(s) - prevLen
+}
- return false
+// private version of Add which doesn't return a value
+func (s threadUnsafeSet[T]) add(v T) {
+ s[v] = struct{}{}
}
-func (set *threadUnsafeSet) Add(i interface{}) bool {
- _, found := (*set)[i]
- (*set)[i] = struct{}{}
- return !found //False if it existed already
+func (s threadUnsafeSet[T]) Cardinality() int {
+ return len(s)
}
-func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
- for _, val := range i {
- if _, ok := (*set)[val]; !ok {
- return false
- }
+func (s threadUnsafeSet[T]) Clear() {
+ // Constructions like this are optimised by compiler, and replaced by
+ // mapclear() function, defined in
+ // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993)
+ for key := range s {
+ delete(s, key)
}
- return true
}
-func (set *threadUnsafeSet) IsSubset(other Set) bool {
- _ = other.(*threadUnsafeSet)
- for elem := range *set {
- if !other.Contains(elem) {
+func (s threadUnsafeSet[T]) Clone() Set[T] {
+ clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality())
+ for elem := range s {
+ clonedSet.add(elem)
+ }
+ return clonedSet
+}
+
+func (s threadUnsafeSet[T]) Contains(v ...T) bool {
+ for _, val := range v {
+ if _, ok := s[val]; !ok {
return false
}
}
return true
}
-func (set *threadUnsafeSet) IsSuperset(other Set) bool {
- return other.IsSubset(set)
+// private version of Contains for a single element v
+func (s threadUnsafeSet[T]) contains(v T) (ok bool) {
+ _, ok = s[v]
+ return ok
+}
+
+func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] {
+ o := other.(threadUnsafeSet[T])
+
+ diff := newThreadUnsafeSet[T]()
+ for elem := range s {
+ if !o.contains(elem) {
+ diff.add(elem)
+ }
+ }
+ return diff
}
-func (set *threadUnsafeSet) Union(other Set) Set {
- o := other.(*threadUnsafeSet)
+func (s threadUnsafeSet[T]) Each(cb func(T) bool) {
+ for elem := range s {
+ if cb(elem) {
+ break
+ }
+ }
+}
- unionedSet := newThreadUnsafeSet()
+func (s threadUnsafeSet[T]) Equal(other Set[T]) bool {
+ o := other.(threadUnsafeSet[T])
- for elem := range *set {
- unionedSet.Add(elem)
+ if s.Cardinality() != other.Cardinality() {
+ return false
}
- for elem := range *o {
- unionedSet.Add(elem)
+ for elem := range s {
+ if !o.contains(elem) {
+ return false
+ }
}
- return &unionedSet
+ return true
}
-func (set *threadUnsafeSet) Intersect(other Set) Set {
- o := other.(*threadUnsafeSet)
+func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] {
+ o := other.(threadUnsafeSet[T])
- intersection := newThreadUnsafeSet()
+ intersection := newThreadUnsafeSet[T]()
// loop over smaller set
- if set.Cardinality() < other.Cardinality() {
- for elem := range *set {
- if other.Contains(elem) {
- intersection.Add(elem)
+ if s.Cardinality() < other.Cardinality() {
+ for elem := range s {
+ if o.contains(elem) {
+ intersection.add(elem)
}
}
} else {
- for elem := range *o {
- if set.Contains(elem) {
- intersection.Add(elem)
+ for elem := range o {
+ if s.contains(elem) {
+ intersection.add(elem)
}
}
}
- return &intersection
+ return intersection
}
-func (set *threadUnsafeSet) Difference(other Set) Set {
- _ = other.(*threadUnsafeSet)
-
- difference := newThreadUnsafeSet()
- for elem := range *set {
- if !other.Contains(elem) {
- difference.Add(elem)
- }
- }
- return &difference
+func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool {
+ return s.Cardinality() < other.Cardinality() && s.IsSubset(other)
}
-func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
- _ = other.(*threadUnsafeSet)
-
- aDiff := set.Difference(other)
- bDiff := other.Difference(set)
- return aDiff.Union(bDiff)
+func (s threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool {
+ return s.Cardinality() > other.Cardinality() && s.IsSuperset(other)
}
-func (set *threadUnsafeSet) Clear() {
- *set = newThreadUnsafeSet()
-}
-
-func (set *threadUnsafeSet) Remove(i interface{}) {
- delete(*set, i)
+func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool {
+ o := other.(threadUnsafeSet[T])
+ if s.Cardinality() > other.Cardinality() {
+ return false
+ }
+ for elem := range s {
+ if !o.contains(elem) {
+ return false
+ }
+ }
+ return true
}
-func (set *threadUnsafeSet) Cardinality() int {
- return len(*set)
+func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool {
+ return other.IsSubset(s)
}
-func (set *threadUnsafeSet) Iter() <-chan interface{} {
- ch := make(chan interface{})
+func (s threadUnsafeSet[T]) Iter() <-chan T {
+ ch := make(chan T)
go func() {
- for elem := range *set {
+ for elem := range s {
ch <- elem
}
close(ch)
@@ -159,88 +192,134 @@ func (set *threadUnsafeSet) Iter() <-chan interface{} {
return ch
}
-func (set *threadUnsafeSet) Equal(other Set) bool {
- _ = other.(*threadUnsafeSet)
+func (s threadUnsafeSet[T]) Iterator() *Iterator[T] {
+ iterator, ch, stopCh := newIterator[T]()
- if set.Cardinality() != other.Cardinality() {
- return false
- }
- for elem := range *set {
- if !other.Contains(elem) {
- return false
+ go func() {
+ L:
+ for elem := range s {
+ select {
+ case <-stopCh:
+ break L
+ case ch <- elem:
+ }
}
+ close(ch)
+ }()
+
+ return iterator
+}
+
+// Pop returns a popped item in case set is not empty, or nil-value of T
+// if set is already empty
+func (s threadUnsafeSet[T]) Pop() (v T, ok bool) {
+ for item := range s {
+ delete(s, item)
+ return item, true
}
- return true
+ return v, false
+}
+
+func (s threadUnsafeSet[T]) Remove(v T) {
+ delete(s, v)
}
-func (set *threadUnsafeSet) Clone() Set {
- clonedSet := newThreadUnsafeSet()
- for elem := range *set {
- clonedSet.Add(elem)
+func (s threadUnsafeSet[T]) RemoveAll(i ...T) {
+ for _, elem := range i {
+ delete(s, elem)
}
- return &clonedSet
}
-func (set *threadUnsafeSet) String() string {
- items := make([]string, 0, len(*set))
+func (s threadUnsafeSet[T]) String() string {
+ items := make([]string, 0, len(s))
- for elem := range *set {
+ for elem := range s {
items = append(items, fmt.Sprintf("%v", elem))
}
return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
}
-func (pair orderedPair) String() string {
- return fmt.Sprintf("(%v, %v)", pair.first, pair.second)
+func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] {
+ o := other.(threadUnsafeSet[T])
+
+ sd := newThreadUnsafeSet[T]()
+ for elem := range s {
+ if !o.contains(elem) {
+ sd.add(elem)
+ }
+ }
+ for elem := range o {
+ if !s.contains(elem) {
+ sd.add(elem)
+ }
+ }
+ return sd
}
-func (set *threadUnsafeSet) PowerSet() Set {
- powSet := NewThreadUnsafeSet()
- nullset := newThreadUnsafeSet()
- powSet.Add(&nullset)
+func (s threadUnsafeSet[T]) ToSlice() []T {
+ keys := make([]T, 0, s.Cardinality())
+ for elem := range s {
+ keys = append(keys, elem)
+ }
- for es := range *set {
- u := newThreadUnsafeSet()
- j := powSet.Iter()
- for er := range j {
- p := newThreadUnsafeSet()
- if reflect.TypeOf(er).Name() == "" {
- k := er.(*threadUnsafeSet)
- for ek := range *(k) {
- p.Add(ek)
- }
- } else {
- p.Add(er)
- }
- p.Add(es)
- u.Add(&p)
- }
+ return keys
+}
- powSet = powSet.Union(&u)
+func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] {
+ o := other.(threadUnsafeSet[T])
+
+ n := s.Cardinality()
+ if o.Cardinality() > n {
+ n = o.Cardinality()
}
+ unionedSet := make(threadUnsafeSet[T], n)
- return powSet
+ for elem := range s {
+ unionedSet.add(elem)
+ }
+ for elem := range o {
+ unionedSet.add(elem)
+ }
+ return unionedSet
}
-func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
- o := other.(*threadUnsafeSet)
- cartProduct := NewThreadUnsafeSet()
+// MarshalJSON creates a JSON array from the set, it marshals all elements
+func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) {
+ items := make([]string, 0, s.Cardinality())
- for i := range *set {
- for j := range *o {
- elem := orderedPair{first: i, second: j}
- cartProduct.Add(elem)
+ for elem := range s {
+ b, err := json.Marshal(elem)
+ if err != nil {
+ return nil, err
}
+
+ items = append(items, string(b))
}
- return cartProduct
+ return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
}
-func (set *threadUnsafeSet) ToSlice() []interface{} {
- keys := make([]interface{}, 0, set.Cardinality())
- for elem := range *set {
- keys = append(keys, elem)
+// UnmarshalJSON recreates a set from a JSON array, it only decodes
+// primitive types. Numbers are decoded as json.Number.
+func (s threadUnsafeSet[T]) UnmarshalJSON(b []byte) error {
+ var i []any
+
+ d := json.NewDecoder(bytes.NewReader(b))
+ d.UseNumber()
+ err := d.Decode(&i)
+ if err != nil {
+ return err
}
- return keys
+ for _, v := range i {
+ switch t := v.(type) {
+ case T:
+ s.add(t)
+ default:
+ // anything else must be skipped.
+ continue
+ }
+ }
+
+ return nil
}
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/share/gocode/src/github.com/deckarep/golang-set/go.mod -rw-r--r-- root/root /usr/share/gocode/src/github.com/deckarep/golang-set/go.sum -rw-r--r-- root/root /usr/share/gocode/src/github.com/deckarep/golang-set/iterator.go -rw-r--r-- root/root /usr/share/gocode/src/github.com/deckarep/golang-set/iterator_example_test.go
No differences were encountered in the control files