diff --git a/slice/slice.go b/slice/slice.go index bb6bdbf..a59d0c8 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -10,6 +10,7 @@ import ( "reflect" "sort" "strings" + "sync" "time" "github.com/duke-git/lancet/v2/random" @@ -21,6 +22,7 @@ import ( var ( memoryHashMap = make(map[string]map[any]int) memoryHashCounter = make(map[string]int) + muForMemoryHash sync.RWMutex ) // Contain check if the target value is in the slice or not. @@ -1174,23 +1176,46 @@ func IndexOf[T comparable](arr []T, val T) int { limit := 10 // gets the hash value of the array as the key of the hash table. key := fmt.Sprintf("%p", arr) + + muForMemoryHash.RLock() // determines whether the hash table is empty. If so, the hash table is created. if memoryHashMap[key] == nil { - memoryHashMap[key] = make(map[any]int) - // iterate through the array, adding the value and index of each element to the hash table. - for i := len(arr) - 1; i >= 0; i-- { - memoryHashMap[key][arr[i]] = i + + muForMemoryHash.RUnlock() + muForMemoryHash.Lock() + + if memoryHashMap[key] == nil { + memoryHashMap[key] = make(map[any]int) + // iterate through the array, adding the value and index of each element to the hash table. + for i := len(arr) - 1; i >= 0; i-- { + memoryHashMap[key][arr[i]] = i + } } + + muForMemoryHash.Unlock() + } else { + muForMemoryHash.RUnlock() } + + muForMemoryHash.Lock() // update the hash table counter. memoryHashCounter[key]++ + muForMemoryHash.Unlock() // use the hash table to find the specified value. If found, the index is returned. - if index, ok := memoryHashMap[key][val]; ok { + muForMemoryHash.RLock() + index, ok := memoryHashMap[key][val] + muForMemoryHash.RUnlock() + + if ok { + muForMemoryHash.RLock() // calculate the memory usage of the hash table. size := len(memoryHashMap) + muForMemoryHash.RUnlock() + // If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared. if size > limit { + muForMemoryHash.Lock() var minKey string var minVal int for k, v := range memoryHashCounter { @@ -1204,6 +1229,7 @@ func IndexOf[T comparable](arr []T, val T) int { } delete(memoryHashMap, minKey) delete(memoryHashCounter, minKey) + muForMemoryHash.Unlock() } return index } diff --git a/slice/slice_concurrent.go b/slice/slice_concurrent.go index 4b3746a..b49b55a 100644 --- a/slice/slice_concurrent.go +++ b/slice/slice_concurrent.go @@ -149,7 +149,7 @@ func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, return result } -// UniqueByParallel removes duplicate elements from the slice by parallel +// UniqueByConcurrent removes duplicate elements from the slice by parallel // The comparator function is used to compare the elements // The numThreads parameter specifies the number of threads to use // If numThreads is less than or equal to 0, it will be set to 1 diff --git a/slice/slice_test.go b/slice/slice_test.go index 9af7930..9191c73 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -6,6 +6,7 @@ import ( "reflect" "strconv" "strings" + "sync" "testing" "github.com/duke-git/lancet/v2/internal" @@ -1367,6 +1368,37 @@ func TestIndexOf(t *testing.T) { assert.Equal(-1, IndexOf(arr3, "r")) assert.Equal(2, memoryHashCounter[key3]) assert.Equal(0, memoryHashCounter[minKey]) + + arr4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + const numGoroutines = 100 + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(i int) { + defer wg.Done() + index := IndexOf(arr4, i%10+1) + assert.Equal(i%10, index) + }(i) + } + wg.Wait() +} + +func BenchmarkIndexOfDifferentSizes(b *testing.B) { + sizes := []int{10, 100, 1000, 10000, 100000} + for _, size := range sizes { + arr := make([]int, size) + for i := 0; i < len(arr); i++ { + arr[i] = i + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = IndexOf(arr, size/2) // 查找数组中间的元素 + } + }) + } } func TestLastIndexOf(t *testing.T) {