diff --git a/docs/maputil.md b/docs/maputil.md index 7bd7b2c..956be9b 100644 --- a/docs/maputil.md +++ b/docs/maputil.md @@ -44,12 +44,19 @@ import ( - [Minus](#Minus) - [IsDisjoint](#IsDisjoint) - [HasKey](#HasKey) +- [NewConcurrentMap](#NewConcurrentMap) +- [ConcurrentMap_Get](#ConcurrentMap_Get) +- [ConcurrentMap_Set](#ConcurrentMap_Set) +- [ConcurrentMap_GetOrSet](#ConcurrentMap_GetOrSet) +- [ConcurrentMap_Delete](#ConcurrentMap_Delete) +- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete) +- [ConcurrentMap_Has](#ConcurrentMap_Has) +- [ConcurrentMap_Range](#ConcurrentMap_Range)
## Documentation - ### MapTo

Rry to map any interface to struct or base type.

@@ -984,3 +991,356 @@ func main() { // false } ``` + +### NewConcurrentMap + +

ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.

+ +Signature: + +```go +// NewConcurrentMap create a ConcurrentMap with specific shard count. +func NewConcurrentMap[K comparable, V any](shardCount int) *ConcurrentMap[K, V] +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + // create a ConcurrentMap whose key type is string, value type is int + cm := maputil.NewConcurrentMap[string, int](100) +} +``` + +### ConcurrentMap_Set + +

Set the value for a key.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Set(key K, value V) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + }(j) + } + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_Get + +

Get the value stored in the map for a key, or nil if no.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Get(key K) (V, bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + }(j) + } + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_GetOrSet + +

Returns the existing value for the key if present. Otherwise, it sets and returns the given value.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) GetOrSet(key K, value V) (actual V, ok bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg sync.WaitGroup + wg.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + val, ok := cm.GetOrSet(fmt.Sprintf("%d", n), n) + fmt.Println(val, ok) + wg.Done() + }(i) + } + wg.Wait() + + // output: (order may change) + // 1 false + // 3 false + // 2 false + // 0 false + // 4 false +} +``` + +### ConcurrentMap_Delete + +

Delete the value for a key.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Delete(key K) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + cm.Delete(fmt.Sprintf("%d", n)) + wg2.Done() + }(i) + } +} +``` + + +### ConcurrentMap_GetAndDelete + +

Returns the existing value for the key if present and then delete the value for the key. Otherwise, do nothing, just return false.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) GetAndDelete(key K) (actual V, ok bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //n, true + + _, ok = cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //false + }(j) + } +} +``` + + +### ConcurrentMap_Has + +

Checks if map has the value for a key.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Has(key K) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + ok := cm.Has(fmt.Sprintf("%d", n)) + fmt.Println(ok) // true + }(j) + } +} +``` + + +### ConcurrentMap_Range + +

Calls iterator sequentially for each key and value present in each of the shards in the map. If iterator returns false, range stops the iteration.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Range(iterator func(key K, value V) bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + cm.Range(func(key string, value int) bool { + fmt.Println(value) + return true + }) +} +``` diff --git a/docs/maputil_zh-CN.md b/docs/maputil_zh-CN.md index f578d78..63f57cc 100644 --- a/docs/maputil_zh-CN.md +++ b/docs/maputil_zh-CN.md @@ -44,6 +44,15 @@ import ( - [Minus](#Minus) - [IsDisjoint](#IsDisjoint) - [HasKey](#HasKey) +- [NewConcurrentMap](#NewConcurrentMap) +- [ConcurrentMap_Get](#ConcurrentMap_Get) +- [ConcurrentMap_Set](#ConcurrentMap_Set) +- [ConcurrentMap_GetOrSet](#ConcurrentMap_GetOrSet) +- [ConcurrentMap_Delete](#ConcurrentMap_Delete) +- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete) +- [ConcurrentMap_Has](#ConcurrentMap_Has) +- [ConcurrentMap_Range](#ConcurrentMap_Range) +
@@ -979,3 +988,357 @@ func main() { // false } ``` + +### NewConcurrentMap + +

ConcurrentMap协程安全的map结构。

+ +函数签名: + +```go +// NewConcurrentMap create a ConcurrentMap with specific shard count. +func NewConcurrentMap[K comparable, V any](shardCount int) *ConcurrentMap[K, V] +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + // create a ConcurrentMap whose key type is string, value type is int + cm := maputil.NewConcurrentMap[string, int](100) +} +``` + +### ConcurrentMap_Set + +

在map中设置key和value。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Set(key K, value V) +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + }(j) + } + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_Get + +

根据key获取value, 如果不存在key,返回零值。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Get(key K) (V, bool) +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + }(j) + } + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_GetOrSet + +

返回键的现有值(如果存在),否则,设置key并返回给定值。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) GetOrSet(key K, value V) (actual V, ok bool) +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg sync.WaitGroup + wg.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + val, ok := cm.GetOrSet(fmt.Sprintf("%d", n), n) + fmt.Println(val, ok) + wg.Done() + }(i) + } + wg.Wait() + + // output: (order may change) + // 1 false + // 3 false + // 2 false + // 0 false + // 4 false +} +``` + +### ConcurrentMap_Delete + +

删除key。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Delete(key K) +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + cm.Delete(fmt.Sprintf("%d", n)) + wg2.Done() + }(i) + } +} +``` + + +### ConcurrentMap_GetAndDelete + +

获取key,然后删除。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) GetAndDelete(key K) (actual V, ok bool) +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //n, true + + _, ok = cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //false + }(j) + } +} +``` + + +### ConcurrentMap_Has + +

验证是否包含key。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Has(key K) bool +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + for j := 0; j < 5; j++ { + go func(n int) { + ok := cm.Has(fmt.Sprintf("%d", n)) + fmt.Println(ok) // true + }(j) + } +} +``` + + +### ConcurrentMap_Range + +

为map中每个键和值顺序调用迭代器。 如果iterator返回false,则停止迭代。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Range(iterator func(key K, value V) bool) +``` + +实例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + cm.Range(func(key string, value int) bool { + fmt.Println(value) + return true + }) +} +``` + diff --git a/maputil/concurrentmap_test.go b/maputil/concurrentmap_test.go index ca75d40..f6eae25 100644 --- a/maputil/concurrentmap_test.go +++ b/maputil/concurrentmap_test.go @@ -24,17 +24,13 @@ func TestConcurrentMap_Set_Get(t *testing.T) { } wg1.Wait() - var wg2 sync.WaitGroup - wg2.Add(10) for j := 0; j < 10; j++ { go func(n int) { val, ok := cm.Get(fmt.Sprintf("%d", n)) assert.Equal(n, val) assert.Equal(true, ok) - wg2.Done() }(j) } - wg2.Wait() } func TestConcurrentMap_GetOrSet(t *testing.T) {