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

More details

Full run details