diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index cc17dcb3737..2c22125e1f6 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -7,28 +7,18 @@ package garray import ( - "bytes" "fmt" - "math" - "sort" - - "github.com/gogf/gf/v2/errors/gcode" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/internal/deepcopy" - "github.com/gogf/gf/v2/internal/empty" - "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/internal/rwmutex" - "github.com/gogf/gf/v2/text/gstr" + "sync" + "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/grand" ) // Array is a golang array with rich features. // It contains a concurrent-safe/unsafe switch, which should be set // when its initialization and cannot be changed then. type Array struct { - mu rwmutex.RWMutex - array []any + *TArray[any] + once sync.Once } // New creates and returns an empty array. @@ -48,8 +38,7 @@ func NewArray(safe ...bool) *Array { // which is false in default. func NewArraySize(size int, cap int, safe ...bool) *Array { return &Array{ - mu: rwmutex.Create(safe...), - array: make([]any, size, cap), + TArray: NewTArraySize[any](size, cap, safe...), } } @@ -85,8 +74,7 @@ func NewFromCopy(array []any, safe ...bool) *Array { // which is false in default. func NewArrayFrom(array []any, safe ...bool) *Array { return &Array{ - mu: rwmutex.Create(safe...), - array: array, + TArray: NewTArrayFrom(array, safe...), } } @@ -96,265 +84,149 @@ func NewArrayFrom(array []any, safe ...bool) *Array { func NewArrayFromCopy(array []any, safe ...bool) *Array { newArray := make([]any, len(array)) copy(newArray, array) - return &Array{ - mu: rwmutex.Create(safe...), - array: newArray, - } + return NewArrayFrom(newArray, safe...) +} + +// lazyInit lazily initializes the array. +func (a *Array) lazyInit() { + a.once.Do(func() { + if a.TArray == nil { + a.TArray = NewTArray[any](false) + } + }) } // At returns the value by the specified index. // If the given `index` is out of range of the array, it returns `nil`. func (a *Array) At(index int) (value any) { - value, _ = a.Get(index) - return + a.lazyInit() + return a.TArray.At(index) } // Get returns the value by the specified index. // If the given `index` is out of range of the array, the `found` is false. func (a *Array) Get(index int) (value any, found bool) { - a.mu.RLock() - defer a.mu.RUnlock() - if index < 0 || index >= len(a.array) { - return nil, false - } - return a.array[index], true + a.lazyInit() + return a.TArray.Get(index) } // Set sets value to specified index. func (a *Array) Set(index int, value any) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - a.array[index] = value - return nil + a.lazyInit() + return a.TArray.Set(index, value) } // SetArray sets the underlying slice array with the given `array`. func (a *Array) SetArray(array []any) *Array { - a.mu.Lock() - defer a.mu.Unlock() - a.array = array + a.lazyInit() + a.TArray.SetArray(array) return a } // Replace replaces the array items by given `array` from the beginning of array. func (a *Array) Replace(array []any) *Array { - a.mu.Lock() - defer a.mu.Unlock() - max := len(array) - if max > len(a.array) { - max = len(a.array) - } - for i := 0; i < max; i++ { - a.array[i] = array[i] - } + a.lazyInit() + a.TArray.Replace(array) return a } // Sum returns the sum of values in an array. func (a *Array) Sum() (sum int) { - a.mu.RLock() - defer a.mu.RUnlock() - for _, v := range a.array { - sum += gconv.Int(v) - } - return + a.lazyInit() + return a.TArray.Sum() } // SortFunc sorts the array by custom function `less`. func (a *Array) SortFunc(less func(v1, v2 any) bool) *Array { - a.mu.Lock() - defer a.mu.Unlock() - sort.Slice(a.array, func(i, j int) bool { - return less(a.array[i], a.array[j]) - }) + a.lazyInit() + a.TArray.SortFunc(less) return a } // InsertBefore inserts the `values` to the front of `index`. func (a *Array) InsertBefore(index int, values ...any) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - rear := append([]any{}, a.array[index:]...) - a.array = append(a.array[0:index], values...) - a.array = append(a.array, rear...) - return nil + a.lazyInit() + return a.TArray.InsertBefore(index, values...) } // InsertAfter inserts the `values` to the back of `index`. func (a *Array) InsertAfter(index int, values ...any) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - rear := append([]any{}, a.array[index+1:]...) - a.array = append(a.array[0:index+1], values...) - a.array = append(a.array, rear...) - return nil + a.lazyInit() + return a.TArray.InsertAfter(index, values...) } // Remove removes an item by index. // If the given `index` is out of range of the array, the `found` is false. func (a *Array) Remove(index int) (value any, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - return a.doRemoveWithoutLock(index) -} - -// doRemoveWithoutLock removes an item by index without lock. -func (a *Array) doRemoveWithoutLock(index int) (value any, found bool) { - if index < 0 || index >= len(a.array) { - return nil, false - } - // Determine array boundaries when deleting to improve deletion efficiency. - if index == 0 { - value := a.array[0] - a.array = a.array[1:] - return value, true - } else if index == len(a.array)-1 { - value := a.array[index] - a.array = a.array[:index] - return value, true - } - // If it is a non-boundary delete, - // it will involve the creation of an array, - // then the deletion is less efficient. - value = a.array[index] - a.array = append(a.array[:index], a.array[index+1:]...) - return value, true + a.lazyInit() + return a.TArray.Remove(index) } // RemoveValue removes an item by value. // It returns true if value is found in the array, or else false if not found. func (a *Array) RemoveValue(value any) bool { - a.mu.Lock() - defer a.mu.Unlock() - if i := a.doSearchWithoutLock(value); i != -1 { - a.doRemoveWithoutLock(i) - return true - } - return false + a.lazyInit() + return a.TArray.RemoveValue(value) } // RemoveValues removes multiple items by `values`. func (a *Array) RemoveValues(values ...any) { - a.mu.Lock() - defer a.mu.Unlock() - for _, value := range values { - if i := a.doSearchWithoutLock(value); i != -1 { - a.doRemoveWithoutLock(i) - } - } + a.lazyInit() + a.TArray.RemoveValues(values...) } // PushLeft pushes one or multiple items to the beginning of array. func (a *Array) PushLeft(value ...any) *Array { - a.mu.Lock() - a.array = append(value, a.array...) - a.mu.Unlock() + a.lazyInit() + a.TArray.PushLeft(value...) return a } // PushRight pushes one or multiple items to the end of array. // It equals to Append. func (a *Array) PushRight(value ...any) *Array { - a.mu.Lock() - a.array = append(a.array, value...) - a.mu.Unlock() + a.lazyInit() + a.TArray.PushRight(value...) return a } // PopRand randomly pops and return an item out of array. // Note that if the array is empty, the `found` is false. func (a *Array) PopRand() (value any, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - return a.doRemoveWithoutLock(grand.Intn(len(a.array))) + a.lazyInit() + return a.TArray.PopRand() } // PopRands randomly pops and returns `size` items out of array. func (a *Array) PopRands(size int) []any { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - if size >= len(a.array) { - size = len(a.array) - } - array := make([]any, size) - for i := 0; i < size; i++ { - array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array))) - } - return array + a.lazyInit() + return a.TArray.PopRands(size) } // PopLeft pops and returns an item from the beginning of array. // Note that if the array is empty, the `found` is false. func (a *Array) PopLeft() (value any, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - if len(a.array) == 0 { - return nil, false - } - value = a.array[0] - a.array = a.array[1:] - return value, true + a.lazyInit() + return a.TArray.PopLeft() } // PopRight pops and returns an item from the end of array. // Note that if the array is empty, the `found` is false. func (a *Array) PopRight() (value any, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - index := len(a.array) - 1 - if index < 0 { - return nil, false - } - value = a.array[index] - a.array = a.array[:index] - return value, true + a.lazyInit() + return a.TArray.PopRight() } // PopLefts pops and returns `size` items from the beginning of array. func (a *Array) PopLefts(size int) []any { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - if size >= len(a.array) { - array := a.array - a.array = a.array[:0] - return array - } - value := a.array[0:size] - a.array = a.array[size:] - return value + a.lazyInit() + return a.TArray.PopLefts(size) } // PopRights pops and returns `size` items from the end of array. func (a *Array) PopRights(size int) []any { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - index := len(a.array) - size - if index <= 0 { - array := a.array - a.array = a.array[:0] - return array - } - value := a.array[index:] - a.array = a.array[:index] - return value + a.lazyInit() + return a.TArray.PopRights(size) } // Range picks and returns items by range, like array[start:end]. @@ -365,26 +237,8 @@ func (a *Array) PopRights(size int) []any { // If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *Array) Range(start int, end ...int) []any { - a.mu.RLock() - defer a.mu.RUnlock() - offsetEnd := len(a.array) - if len(end) > 0 && end[0] < offsetEnd { - offsetEnd = end[0] - } - if start > offsetEnd { - return nil - } - if start < 0 { - start = 0 - } - array := ([]any)(nil) - if a.mu.IsSafe() { - array = make([]any, offsetEnd-start) - copy(array, a.array[start:offsetEnd]) - } else { - array = a.array[start:offsetEnd] - } - return array + a.lazyInit() + return a.TArray.Range(start, end...) } // SubSlice returns a slice of elements from the array as specified @@ -401,69 +255,29 @@ func (a *Array) Range(start int, end ...int) []any { // // Any possibility crossing the left border of array, it will fail. func (a *Array) SubSlice(offset int, length ...int) []any { - a.mu.RLock() - defer a.mu.RUnlock() - size := len(a.array) - if len(length) > 0 { - size = length[0] - } - if offset > len(a.array) { - return nil - } - if offset < 0 { - offset = len(a.array) + offset - if offset < 0 { - return nil - } - } - if size < 0 { - offset += size - size = -size - if offset < 0 { - return nil - } - } - end := offset + size - if end > len(a.array) { - end = len(a.array) - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]any, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:end] - } + a.lazyInit() + return a.TArray.SubSlice(offset, length...) } // Append is alias of PushRight, please See PushRight. func (a *Array) Append(value ...any) *Array { - a.PushRight(value...) + a.lazyInit() + a.TArray.Append(value...) return a } // Len returns the length of array. func (a *Array) Len() int { - a.mu.RLock() - length := len(a.array) - a.mu.RUnlock() - return length + a.lazyInit() + return a.TArray.Len() } // Slice returns the underlying data of array. // Note that, if it's in concurrent-safe usage, it returns a copy of underlying data, // or else a pointer to the underlying data. func (a *Array) Slice() []any { - if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array := make([]any, len(a.array)) - copy(array, a.array) - return array - } else { - return a.array - } + a.lazyInit() + return a.TArray.Slice() } // Interfaces returns current array as []any. @@ -473,89 +287,49 @@ func (a *Array) Interfaces() []any { // Clone returns a new array, which is a copy of current array. func (a *Array) Clone() (newArray *Array) { - a.mu.RLock() - array := make([]any, len(a.array)) - copy(array, a.array) - a.mu.RUnlock() - return NewArrayFrom(array, a.mu.IsSafe()) + a.lazyInit() + return &Array{TArray: a.TArray.Clone()} } // Clear deletes all items of current array. func (a *Array) Clear() *Array { - a.mu.Lock() - if len(a.array) > 0 { - a.array = make([]any, 0) - } - a.mu.Unlock() + a.lazyInit() + a.TArray.Clear() return a } // Contains checks whether a value exists in the array. func (a *Array) Contains(value any) bool { - return a.Search(value) != -1 + a.lazyInit() + return a.TArray.Contains(value) } // Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *Array) Search(value any) int { - a.mu.RLock() - defer a.mu.RUnlock() - return a.doSearchWithoutLock(value) -} - -func (a *Array) doSearchWithoutLock(value any) int { - if len(a.array) == 0 { - return -1 - } - result := -1 - for index, v := range a.array { - if v == value { - result = index - break - } - } - return result + a.lazyInit() + return a.TArray.Search(value) } // Unique uniques the array, clear repeated items. // Example: [1,1,2,3,2] -> [1,2,3] func (a *Array) Unique() *Array { - a.mu.Lock() - defer a.mu.Unlock() - if len(a.array) == 0 { - return a - } - var ( - ok bool - temp any - uniqueSet = make(map[any]struct{}) - uniqueArray = make([]any, 0, len(a.array)) - ) - for i := 0; i < len(a.array); i++ { - temp = a.array[i] - if _, ok = uniqueSet[temp]; ok { - continue - } - uniqueSet[temp] = struct{}{} - uniqueArray = append(uniqueArray, temp) - } - a.array = uniqueArray + a.lazyInit() + a.TArray.Unique() return a } // LockFunc locks writing by callback function `f`. func (a *Array) LockFunc(f func(array []any)) *Array { - a.mu.Lock() - defer a.mu.Unlock() - f(a.array) + a.lazyInit() + a.TArray.LockFunc(f) return a } // RLockFunc locks reading by callback function `f`. func (a *Array) RLockFunc(f func(array []any)) *Array { - a.mu.RLock() - defer a.mu.RUnlock() - f(a.array) + a.lazyInit() + a.TArray.RLockFunc(f) return a } @@ -564,48 +338,23 @@ func (a *Array) RLockFunc(f func(array []any)) *Array { // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *Array) Merge(array any) *Array { + a.lazyInit() return a.Append(gconv.Interfaces(array)...) } // Fill fills an array with num entries of the value `value`, // keys starting at the `startIndex` parameter. func (a *Array) Fill(startIndex int, num int, value any) error { - a.mu.Lock() - defer a.mu.Unlock() - if startIndex < 0 || startIndex > len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) - } - for i := startIndex; i < startIndex+num; i++ { - if i > len(a.array)-1 { - a.array = append(a.array, value) - } else { - a.array[i] = value - } - } - return nil + a.lazyInit() + return a.TArray.Fill(startIndex, num, value) } // Chunk splits an array into multiple arrays, // the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *Array) Chunk(size int) [][]any { - if size < 1 { - return nil - } - a.mu.RLock() - defer a.mu.RUnlock() - length := len(a.array) - chunks := int(math.Ceil(float64(length) / float64(size))) - var n [][]any - for i, end := 0, 0; chunks > 0; chunks-- { - end = (i + 1) * size - if end > length { - end = length - } - n = append(n, a.array[i*size:end]) - i++ - } - return n + a.lazyInit() + return a.TArray.Chunk(size) } // Pad pads array to the specified length with `value`. @@ -613,98 +362,47 @@ func (a *Array) Chunk(size int) [][]any { // If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *Array) Pad(size int, val any) *Array { - a.mu.Lock() - defer a.mu.Unlock() - if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) { - return a - } - n := size - if size < 0 { - n = -size - } - n -= len(a.array) - tmp := make([]any, n) - for i := 0; i < n; i++ { - tmp[i] = val - } - if size > 0 { - a.array = append(a.array, tmp...) - } else { - a.array = append(tmp, a.array...) - } + a.lazyInit() + a.TArray.Pad(size, val) return a } // Rand randomly returns one item from array(no deleting). func (a *Array) Rand() (value any, found bool) { - a.mu.RLock() - defer a.mu.RUnlock() - if len(a.array) == 0 { - return nil, false - } - return a.array[grand.Intn(len(a.array))], true + a.lazyInit() + return a.TArray.Rand() } // Rands randomly returns `size` items from array(no deleting). func (a *Array) Rands(size int) []any { - a.mu.RLock() - defer a.mu.RUnlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - array := make([]any, size) - for i := 0; i < size; i++ { - array[i] = a.array[grand.Intn(len(a.array))] - } - return array + a.lazyInit() + return a.TArray.Rands(size) } // Shuffle randomly shuffles the array. func (a *Array) Shuffle() *Array { - a.mu.Lock() - defer a.mu.Unlock() - for i, v := range grand.Perm(len(a.array)) { - a.array[i], a.array[v] = a.array[v], a.array[i] - } + a.lazyInit() + a.TArray.Shuffle() return a } // Reverse makes array with elements in reverse order. func (a *Array) Reverse() *Array { - a.mu.Lock() - defer a.mu.Unlock() - for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 { - a.array[i], a.array[j] = a.array[j], a.array[i] - } + a.lazyInit() + a.TArray.Reverse() return a } // Join joins array elements with a string `glue`. func (a *Array) Join(glue string) string { - a.mu.RLock() - defer a.mu.RUnlock() - if len(a.array) == 0 { - return "" - } - buffer := bytes.NewBuffer(nil) - for k, v := range a.array { - buffer.WriteString(gconv.String(v)) - if k != len(a.array)-1 { - buffer.WriteString(glue) - } - } - return buffer.String() + a.lazyInit() + return a.TArray.Join(glue) } // CountValues counts the number of occurrences of all values in the array. func (a *Array) CountValues() map[any]int { - m := make(map[any]int) - a.mu.RLock() - defer a.mu.RUnlock() - for _, v := range a.array { - m[v]++ - } - return m + a.lazyInit() + return a.TArray.CountValues() } // Iterator is alias of IteratorAsc. @@ -715,25 +413,15 @@ func (a *Array) Iterator(f func(k int, v any) bool) { // IteratorAsc iterates the array readonly in ascending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *Array) IteratorAsc(f func(k int, v any) bool) { - a.mu.RLock() - defer a.mu.RUnlock() - for k, v := range a.array { - if !f(k, v) { - break - } - } + a.lazyInit() + a.TArray.IteratorAsc(f) } // IteratorDesc iterates the array readonly in descending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *Array) IteratorDesc(f func(k int, v any) bool) { - a.mu.RLock() - defer a.mu.RUnlock() - for i := len(a.array) - 1; i >= 0; i-- { - if !f(i, a.array[i]) { - break - } - } + a.lazyInit() + a.TArray.IteratorDesc(f) } // String returns current array as a string, which implements like json.Marshal does. @@ -741,118 +429,64 @@ func (a *Array) String() string { if a == nil { return "" } - a.mu.RLock() - defer a.mu.RUnlock() - buffer := bytes.NewBuffer(nil) - buffer.WriteByte('[') - s := "" - for k, v := range a.array { - s = gconv.String(v) - if gstr.IsNumeric(s) { - buffer.WriteString(s) - } else { - buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`) - } - if k != len(a.array)-1 { - buffer.WriteByte(',') - } - } - buffer.WriteByte(']') - return buffer.String() + a.lazyInit() + return a.TArray.String() } // MarshalJSON implements the interface MarshalJSON for json.Marshal. // Note that do not use pointer as its receiver here. func (a Array) MarshalJSON() ([]byte, error) { - a.mu.RLock() - defer a.mu.RUnlock() - return json.Marshal(a.array) + a.lazyInit() + return a.TArray.MarshalJSON() } // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (a *Array) UnmarshalJSON(b []byte) error { - if a.array == nil { - a.array = make([]any, 0) - } - a.mu.Lock() - defer a.mu.Unlock() - if err := json.UnmarshalUseNumber(b, &a.array); err != nil { - return err - } - return nil + a.lazyInit() + return a.TArray.UnmarshalJSON(b) } // UnmarshalValue is an interface implement which sets any type of value for array. func (a *Array) UnmarshalValue(value any) error { - a.mu.Lock() - defer a.mu.Unlock() - switch value.(type) { - case string, []byte: - return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) - default: - a.array = gconv.SliceAny(value) - } - return nil + a.lazyInit() + return a.TArray.UnmarshalValue(value) } // Filter iterates array and filters elements using custom callback function. // It removes the element from array if callback function `filter` returns true, // it or else does nothing and continues iterating. func (a *Array) Filter(filter func(index int, value any) bool) *Array { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if filter(i, a.array[i]) { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.Filter(filter) return a } // FilterNil removes all nil value of the array. func (a *Array) FilterNil() *Array { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if empty.IsNil(a.array[i]) { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.FilterNil() return a } // FilterEmpty removes all empty value of the array. // Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty. func (a *Array) FilterEmpty() *Array { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if empty.IsEmpty(a.array[i]) { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.FilterEmpty() return a } // Walk applies a user supplied function `f` to every item of array. func (a *Array) Walk(f func(value any) any) *Array { - a.mu.Lock() - defer a.mu.Unlock() - for i, v := range a.array { - a.array[i] = f(v) - } + a.lazyInit() + a.TArray.Walk(f) return a } // IsEmpty checks whether the array is empty. func (a *Array) IsEmpty() bool { - return a.Len() == 0 + a.lazyInit() + return a.TArray.IsEmpty() } // DeepCopy implements interface for deep copy of current type. @@ -860,11 +494,8 @@ func (a *Array) DeepCopy() any { if a == nil { return nil } - a.mu.RLock() - defer a.mu.RUnlock() - newSlice := make([]any, len(a.array)) - for i, v := range a.array { - newSlice[i] = deepcopy.Copy(v) + a.lazyInit() + return &Array{ + TArray: a.TArray.DeepCopy().(*TArray[any]), } - return NewArrayFrom(newSlice, a.mu.IsSafe()) } diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 87526a9f220..aa7eca72a1b 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -7,25 +7,19 @@ package garray import ( - "bytes" "fmt" - "math" "sort" + "sync" - "github.com/gogf/gf/v2/errors/gcode" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/internal/rwmutex" "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/grand" ) // IntArray is a golang int array with rich features. // It contains a concurrent-safe/unsafe switch, which should be set // when its initialization and cannot be changed then. type IntArray struct { - mu rwmutex.RWMutex - array []int + *TArray[int] + once sync.Once } // NewIntArray creates and returns an empty array. @@ -40,8 +34,7 @@ func NewIntArray(safe ...bool) *IntArray { // which is false in default. func NewIntArraySize(size int, cap int, safe ...bool) *IntArray { return &IntArray{ - mu: rwmutex.Create(safe...), - array: make([]int, size, cap), + TArray: NewTArraySize[int](size, cap, safe...), } } @@ -65,8 +58,7 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray { // which is false in default. func NewIntArrayFrom(array []int, safe ...bool) *IntArray { return &IntArray{ - mu: rwmutex.Create(safe...), - array: array, + TArray: NewTArrayFrom(array, safe...), } } @@ -76,78 +68,66 @@ func NewIntArrayFrom(array []int, safe ...bool) *IntArray { func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray { newArray := make([]int, len(array)) copy(newArray, array) - return &IntArray{ - mu: rwmutex.Create(safe...), - array: newArray, - } + return NewIntArrayFrom(newArray, safe...) +} + +// lazyInit lazily initializes the array. +func (a *IntArray) lazyInit() { + a.once.Do(func() { + if a.TArray == nil { + a.TArray = NewTArray[int](false) + } + }) } // At returns the value by the specified index. // If the given `index` is out of range of the array, it returns `0`. func (a *IntArray) At(index int) (value int) { - value, _ = a.Get(index) - return + a.lazyInit() + return a.TArray.At(index) } // Get returns the value by the specified index. // If the given `index` is out of range of the array, the `found` is false. func (a *IntArray) Get(index int) (value int, found bool) { - a.mu.RLock() - defer a.mu.RUnlock() - if index < 0 || index >= len(a.array) { - return 0, false - } - return a.array[index], true + a.lazyInit() + return a.TArray.Get(index) } // Set sets value to specified index. func (a *IntArray) Set(index int, value int) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - a.array[index] = value - return nil + a.lazyInit() + return a.TArray.Set(index, value) } // SetArray sets the underlying slice array with the given `array`. func (a *IntArray) SetArray(array []int) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - a.array = array + a.lazyInit() + a.TArray.SetArray(array) return a } // Replace replaces the array items by given `array` from the beginning of array. func (a *IntArray) Replace(array []int) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - max := len(array) - if max > len(a.array) { - max = len(a.array) - } - for i := 0; i < max; i++ { - a.array[i] = array[i] - } + a.lazyInit() + a.TArray.Replace(array) return a } // Sum returns the sum of values in an array. func (a *IntArray) Sum() (sum int) { - a.mu.RLock() - defer a.mu.RUnlock() - for _, v := range a.array { - sum += v - } - return + a.lazyInit() + return a.TArray.Sum() } // Sort sorts the array in increasing order. // The parameter `reverse` controls whether sort in increasing order(default) or decreasing order. func (a *IntArray) Sort(reverse ...bool) *IntArray { + a.lazyInit() + a.mu.Lock() defer a.mu.Unlock() + if len(reverse) > 0 && reverse[0] { sort.Slice(a.array, func(i, j int) bool { return a.array[i] >= a.array[j] @@ -160,210 +140,101 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray { // SortFunc sorts the array by custom function `less`. func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - sort.Slice(a.array, func(i, j int) bool { - return less(a.array[i], a.array[j]) - }) + a.lazyInit() + a.TArray.SortFunc(less) return a } // InsertBefore inserts the `values` to the front of `index`. func (a *IntArray) InsertBefore(index int, values ...int) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef( - gcode.CodeInvalidParameter, - "index %d out of array range %d", - index, len(a.array), - ) - } - rear := append([]int{}, a.array[index:]...) - a.array = append(a.array[0:index], values...) - a.array = append(a.array, rear...) - return nil + a.lazyInit() + return a.TArray.InsertBefore(index, values...) } // InsertAfter inserts the `value` to the back of `index`. func (a *IntArray) InsertAfter(index int, values ...int) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef( - gcode.CodeInvalidParameter, - "index %d out of array range %d", - index, len(a.array), - ) - } - rear := append([]int{}, a.array[index+1:]...) - a.array = append(a.array[0:index+1], values...) - a.array = append(a.array, rear...) - return nil + a.lazyInit() + return a.TArray.InsertAfter(index, values...) } // Remove removes an item by index. // If the given `index` is out of range of the array, the `found` is false. func (a *IntArray) Remove(index int) (value int, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - return a.doRemoveWithoutLock(index) -} - -// doRemoveWithoutLock removes an item by index without lock. -func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) { - if index < 0 || index >= len(a.array) { - return 0, false - } - // Determine array boundaries when deleting to improve deletion efficiency. - if index == 0 { - value := a.array[0] - a.array = a.array[1:] - return value, true - } else if index == len(a.array)-1 { - value := a.array[index] - a.array = a.array[:index] - return value, true - } - // If it is a non-boundary delete, - // it will involve the creation of an array, - // then the deletion is less efficient. - value = a.array[index] - a.array = append(a.array[:index], a.array[index+1:]...) - return value, true + a.lazyInit() + return a.TArray.Remove(index) } // RemoveValue removes an item by value. // It returns true if value is found in the array, or else false if not found. func (a *IntArray) RemoveValue(value int) bool { - a.mu.Lock() - defer a.mu.Unlock() - if i := a.doSearchWithoutLock(value); i != -1 { - a.doRemoveWithoutLock(i) - return true - } - return false + a.lazyInit() + return a.TArray.RemoveValue(value) } // RemoveValues removes multiple items by `values`. func (a *IntArray) RemoveValues(values ...int) { - a.mu.Lock() - defer a.mu.Unlock() - for _, value := range values { - if i := a.doSearchWithoutLock(value); i != -1 { - a.doRemoveWithoutLock(i) - } - } + a.lazyInit() + a.TArray.RemoveValues(values...) } // PushLeft pushes one or multiple items to the beginning of array. func (a *IntArray) PushLeft(value ...int) *IntArray { - a.mu.Lock() - a.array = append(value, a.array...) - a.mu.Unlock() + a.lazyInit() + a.TArray.PushLeft(value...) return a } // PushRight pushes one or multiple items to the end of array. // It equals to Append. func (a *IntArray) PushRight(value ...int) *IntArray { - a.mu.Lock() - a.array = append(a.array, value...) - a.mu.Unlock() + a.lazyInit() + a.TArray.PushRight(value...) return a } // PopLeft pops and returns an item from the beginning of array. // Note that if the array is empty, the `found` is false. func (a *IntArray) PopLeft() (value int, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - if len(a.array) == 0 { - return 0, false - } - value = a.array[0] - a.array = a.array[1:] - return value, true + a.lazyInit() + return a.TArray.PopLeft() } // PopRight pops and returns an item from the end of array. // Note that if the array is empty, the `found` is false. func (a *IntArray) PopRight() (value int, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - index := len(a.array) - 1 - if index < 0 { - return 0, false - } - value = a.array[index] - a.array = a.array[:index] - return value, true + a.lazyInit() + return a.TArray.PopRight() } // PopRand randomly pops and return an item out of array. // Note that if the array is empty, the `found` is false. func (a *IntArray) PopRand() (value int, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - return a.doRemoveWithoutLock(grand.Intn(len(a.array))) + a.lazyInit() + return a.TArray.PopRand() } // PopRands randomly pops and returns `size` items out of array. // If the given `size` is greater than size of the array, it returns all elements of the array. // Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *IntArray) PopRands(size int) []int { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - if size >= len(a.array) { - size = len(a.array) - } - array := make([]int, size) - for i := 0; i < size; i++ { - array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array))) - } - return array + a.lazyInit() + return a.TArray.PopRands(size) } // PopLefts pops and returns `size` items from the beginning of array. // If the given `size` is greater than size of the array, it returns all elements of the array. // Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *IntArray) PopLefts(size int) []int { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - if size >= len(a.array) { - array := a.array - a.array = a.array[:0] - return array - } - value := a.array[0:size] - a.array = a.array[size:] - return value + a.lazyInit() + return a.TArray.PopLefts(size) } // PopRights pops and returns `size` items from the end of array. // If the given `size` is greater than size of the array, it returns all elements of the array. // Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *IntArray) PopRights(size int) []int { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - index := len(a.array) - size - if index <= 0 { - array := a.array - a.array = a.array[:0] - return array - } - value := a.array[index:] - a.array = a.array[:index] - return value + a.lazyInit() + return a.TArray.PopRights(size) } // Range picks and returns items by range, like array[start:end]. @@ -374,26 +245,8 @@ func (a *IntArray) PopRights(size int) []int { // If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *IntArray) Range(start int, end ...int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - offsetEnd := len(a.array) - if len(end) > 0 && end[0] < offsetEnd { - offsetEnd = end[0] - } - if start > offsetEnd { - return nil - } - if start < 0 { - start = 0 - } - array := ([]int)(nil) - if a.mu.IsSafe() { - array = make([]int, offsetEnd-start) - copy(array, a.array[start:offsetEnd]) - } else { - array = a.array[start:offsetEnd] - } - return array + a.lazyInit() + return a.TArray.Range(start, end...) } // SubSlice returns a slice of elements from the array as specified @@ -410,170 +263,84 @@ func (a *IntArray) Range(start int, end ...int) []int { // // Any possibility crossing the left border of array, it will fail. func (a *IntArray) SubSlice(offset int, length ...int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - size := len(a.array) - if len(length) > 0 { - size = length[0] - } - if offset > len(a.array) { - return nil - } - if offset < 0 { - offset = len(a.array) + offset - if offset < 0 { - return nil - } - } - if size < 0 { - offset += size - size = -size - if offset < 0 { - return nil - } - } - end := offset + size - if end > len(a.array) { - end = len(a.array) - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]int, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:end] - } + a.lazyInit() + return a.TArray.SubSlice(offset, length...) } // Append is alias of PushRight,please See PushRight. func (a *IntArray) Append(value ...int) *IntArray { - a.mu.Lock() - a.array = append(a.array, value...) - a.mu.Unlock() + a.lazyInit() + a.TArray.Append(value...) return a } // Len returns the length of array. func (a *IntArray) Len() int { - a.mu.RLock() - length := len(a.array) - a.mu.RUnlock() - return length + a.lazyInit() + return a.TArray.Len() } // Slice returns the underlying data of array. // Note that, if it's in concurrent-safe usage, it returns a copy of underlying data, // or else a pointer to the underlying data. func (a *IntArray) Slice() []int { - array := ([]int)(nil) - if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]int, len(a.array)) - copy(array, a.array) - } else { - array = a.array - } - return array + a.lazyInit() + return a.TArray.Slice() } // Interfaces returns current array as []any. func (a *IntArray) Interfaces() []any { - a.mu.RLock() - defer a.mu.RUnlock() - array := make([]any, len(a.array)) - for k, v := range a.array { - array[k] = v - } - return array + a.lazyInit() + return a.TArray.Interfaces() } // Clone returns a new array, which is a copy of current array. func (a *IntArray) Clone() (newArray *IntArray) { - a.mu.RLock() - array := make([]int, len(a.array)) - copy(array, a.array) - a.mu.RUnlock() - return NewIntArrayFrom(array, a.mu.IsSafe()) + a.lazyInit() + return &IntArray{ + TArray: a.TArray.Clone(), + } } // Clear deletes all items of current array. func (a *IntArray) Clear() *IntArray { - a.mu.Lock() - if len(a.array) > 0 { - a.array = make([]int, 0) - } - a.mu.Unlock() + a.lazyInit() + a.TArray.Clear() return a } // Contains checks whether a value exists in the array. func (a *IntArray) Contains(value int) bool { - return a.Search(value) != -1 + a.lazyInit() + return a.TArray.Contains(value) } // Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *IntArray) Search(value int) int { - a.mu.RLock() - defer a.mu.RUnlock() - return a.doSearchWithoutLock(value) -} - -func (a *IntArray) doSearchWithoutLock(value int) int { - if len(a.array) == 0 { - return -1 - } - result := -1 - for index, v := range a.array { - if v == value { - result = index - break - } - } - return result + a.lazyInit() + return a.TArray.Search(value) } // Unique uniques the array, clear repeated items. // Example: [1,1,2,3,2] -> [1,2,3] func (a *IntArray) Unique() *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - if len(a.array) == 0 { - return a - } - var ( - ok bool - temp int - uniqueSet = make(map[int]struct{}) - uniqueArray = make([]int, 0, len(a.array)) - ) - for i := 0; i < len(a.array); i++ { - temp = a.array[i] - if _, ok = uniqueSet[temp]; ok { - continue - } - uniqueSet[temp] = struct{}{} - uniqueArray = append(uniqueArray, temp) - } - a.array = uniqueArray + a.lazyInit() + a.TArray.Unique() return a } // LockFunc locks writing by callback function `f`. func (a *IntArray) LockFunc(f func(array []int)) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - f(a.array) + a.lazyInit() + a.TArray.LockFunc(f) return a } // RLockFunc locks reading by callback function `f`. func (a *IntArray) RLockFunc(f func(array []int)) *IntArray { - a.mu.RLock() - defer a.mu.RUnlock() - f(a.array) + a.lazyInit() + a.TArray.RLockFunc(f) return a } @@ -588,46 +355,16 @@ func (a *IntArray) Merge(array any) *IntArray { // Fill fills an array with num entries of the value `value`, // keys starting at the `startIndex` parameter. func (a *IntArray) Fill(startIndex int, num int, value int) error { - a.mu.Lock() - defer a.mu.Unlock() - if startIndex < 0 || startIndex > len(a.array) { - return gerror.NewCodef( - gcode.CodeInvalidParameter, - "index %d out of array range %d", - startIndex, len(a.array), - ) - } - for i := startIndex; i < startIndex+num; i++ { - if i > len(a.array)-1 { - a.array = append(a.array, value) - } else { - a.array[i] = value - } - } - return nil + a.lazyInit() + return a.TArray.Fill(startIndex, num, value) } // Chunk splits an array into multiple arrays, // the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *IntArray) Chunk(size int) [][]int { - if size < 1 { - return nil - } - a.mu.RLock() - defer a.mu.RUnlock() - length := len(a.array) - chunks := int(math.Ceil(float64(length) / float64(size))) - var n [][]int - for i, end := 0, 0; chunks > 0; chunks-- { - end = (i + 1) * size - if end > length { - end = length - } - n = append(n, a.array[i*size:end]) - i++ - } - return n + a.lazyInit() + return a.TArray.Chunk(size) } // Pad pads array to the specified length with `value`. @@ -635,98 +372,47 @@ func (a *IntArray) Chunk(size int) [][]int { // If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *IntArray) Pad(size int, value int) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) { - return a - } - n := size - if size < 0 { - n = -size - } - n -= len(a.array) - tmp := make([]int, n) - for i := 0; i < n; i++ { - tmp[i] = value - } - if size > 0 { - a.array = append(a.array, tmp...) - } else { - a.array = append(tmp, a.array...) - } + a.lazyInit() + a.TArray.Pad(size, value) return a } // Rand randomly returns one item from array(no deleting). func (a *IntArray) Rand() (value int, found bool) { - a.mu.RLock() - defer a.mu.RUnlock() - if len(a.array) == 0 { - return 0, false - } - return a.array[grand.Intn(len(a.array))], true + a.lazyInit() + return a.TArray.Rand() } // Rands randomly returns `size` items from array(no deleting). func (a *IntArray) Rands(size int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - array := make([]int, size) - for i := 0; i < size; i++ { - array[i] = a.array[grand.Intn(len(a.array))] - } - return array + a.lazyInit() + return a.TArray.Rands(size) } // Shuffle randomly shuffles the array. func (a *IntArray) Shuffle() *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - for i, v := range grand.Perm(len(a.array)) { - a.array[i], a.array[v] = a.array[v], a.array[i] - } + a.lazyInit() + a.TArray.Shuffle() return a } // Reverse makes array with elements in reverse order. func (a *IntArray) Reverse() *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 { - a.array[i], a.array[j] = a.array[j], a.array[i] - } + a.lazyInit() + a.TArray.Reverse() return a } // Join joins array elements with a string `glue`. func (a *IntArray) Join(glue string) string { - a.mu.RLock() - defer a.mu.RUnlock() - if len(a.array) == 0 { - return "" - } - buffer := bytes.NewBuffer(nil) - for k, v := range a.array { - buffer.WriteString(gconv.String(v)) - if k != len(a.array)-1 { - buffer.WriteString(glue) - } - } - return buffer.String() + a.lazyInit() + return a.TArray.Join(glue) } // CountValues counts the number of occurrences of all values in the array. func (a *IntArray) CountValues() map[int]int { - m := make(map[int]int) - a.mu.RLock() - defer a.mu.RUnlock() - for _, v := range a.array { - m[v]++ - } - return m + a.lazyInit() + return a.TArray.CountValues() } // Iterator is alias of IteratorAsc. @@ -737,25 +423,15 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) { // IteratorAsc iterates the array readonly in ascending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *IntArray) IteratorAsc(f func(k int, v int) bool) { - a.mu.RLock() - defer a.mu.RUnlock() - for k, v := range a.array { - if !f(k, v) { - break - } - } + a.lazyInit() + a.TArray.IteratorAsc(f) } // IteratorDesc iterates the array readonly in descending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *IntArray) IteratorDesc(f func(k int, v int) bool) { - a.mu.RLock() - defer a.mu.RUnlock() - for i := len(a.array) - 1; i >= 0; i-- { - if !f(i, a.array[i]) { - break - } - } + a.lazyInit() + a.TArray.IteratorDesc(f) } // String returns current array as a string, which implements like json.Marshal does. @@ -769,80 +445,49 @@ func (a *IntArray) String() string { // MarshalJSON implements the interface MarshalJSON for json.Marshal. // Note that do not use pointer as its receiver here. func (a IntArray) MarshalJSON() ([]byte, error) { - a.mu.RLock() - defer a.mu.RUnlock() - return json.Marshal(a.array) + a.lazyInit() + return a.TArray.MarshalJSON() } // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (a *IntArray) UnmarshalJSON(b []byte) error { - if a.array == nil { - a.array = make([]int, 0) - } - a.mu.Lock() - defer a.mu.Unlock() - if err := json.UnmarshalUseNumber(b, &a.array); err != nil { - return err - } - return nil + a.lazyInit() + return a.TArray.UnmarshalJSON(b) } // UnmarshalValue is an interface implement which sets any type of value for array. func (a *IntArray) UnmarshalValue(value any) error { - a.mu.Lock() - defer a.mu.Unlock() - switch value.(type) { - case string, []byte: - return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) - default: - a.array = gconv.SliceInt(value) - } - return nil + a.lazyInit() + return a.TArray.UnmarshalValue(value) } // Filter iterates array and filters elements using custom callback function. // It removes the element from array if callback function `filter` returns true, // it or else does nothing and continues iterating. func (a *IntArray) Filter(filter func(index int, value int) bool) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if filter(i, a.array[i]) { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.Filter(filter) return a } // FilterEmpty removes all zero value of the array. func (a *IntArray) FilterEmpty() *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if a.array[i] == 0 { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.FilterEmpty() return a } // Walk applies a user supplied function `f` to every item of array. func (a *IntArray) Walk(f func(value int) int) *IntArray { - a.mu.Lock() - defer a.mu.Unlock() - for i, v := range a.array { - a.array[i] = f(v) - } + a.lazyInit() + a.TArray.Walk(f) return a } // IsEmpty checks whether the array is empty. func (a *IntArray) IsEmpty() bool { - return a.Len() == 0 + a.lazyInit() + return a.TArray.IsEmpty() } // DeepCopy implements interface for deep copy of current type. @@ -850,9 +495,8 @@ func (a *IntArray) DeepCopy() any { if a == nil { return nil } - a.mu.RLock() - defer a.mu.RUnlock() - newSlice := make([]int, len(a.array)) - copy(newSlice, a.array) - return NewIntArrayFrom(newSlice, a.mu.IsSafe()) + a.lazyInit() + return &IntArray{ + TArray: a.TArray.DeepCopy().(*TArray[int]), + } } diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 06b61c3a31b..cb54403266e 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -8,25 +8,20 @@ package garray import ( "bytes" - "math" "sort" "strings" + "sync" - "github.com/gogf/gf/v2/errors/gcode" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/internal/rwmutex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/grand" ) // StrArray is a golang string array with rich features. // It contains a concurrent-safe/unsafe switch, which should be set // when its initialization and cannot be changed then. type StrArray struct { - mu rwmutex.RWMutex - array []string + *TArray[string] + once sync.Once } // NewStrArray creates and returns an empty array. @@ -41,8 +36,7 @@ func NewStrArray(safe ...bool) *StrArray { // which is false in default. func NewStrArraySize(size int, cap int, safe ...bool) *StrArray { return &StrArray{ - mu: rwmutex.Create(safe...), - array: make([]string, size, cap), + TArray: NewTArraySize[string](size, cap, safe...), } } @@ -51,8 +45,7 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray { // which is false in default. func NewStrArrayFrom(array []string, safe ...bool) *StrArray { return &StrArray{ - mu: rwmutex.Create(safe...), - array: array, + TArray: NewTArrayFrom(array, safe...), } } @@ -62,77 +55,64 @@ func NewStrArrayFrom(array []string, safe ...bool) *StrArray { func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray { newArray := make([]string, len(array)) copy(newArray, array) - return &StrArray{ - mu: rwmutex.Create(safe...), - array: newArray, - } + return NewStrArrayFrom(newArray, safe...) +} + +// lazyInit lazily initializes the array. +func (a *StrArray) lazyInit() { + a.once.Do(func() { + if a.TArray == nil { + a.TArray = NewTArray[string](false) + } + }) } // At returns the value by the specified index. // If the given `index` is out of range of the array, it returns an empty string. func (a *StrArray) At(index int) (value string) { - value, _ = a.Get(index) - return + a.lazyInit() + return a.TArray.At(index) } // Get returns the value by the specified index. // If the given `index` is out of range of the array, the `found` is false. func (a *StrArray) Get(index int) (value string, found bool) { - a.mu.RLock() - defer a.mu.RUnlock() - if index < 0 || index >= len(a.array) { - return "", false - } - return a.array[index], true + a.lazyInit() + return a.TArray.Get(index) } // Set sets value to specified index. func (a *StrArray) Set(index int, value string) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - a.array[index] = value - return nil + a.lazyInit() + return a.TArray.Set(index, value) } // SetArray sets the underlying slice array with the given `array`. func (a *StrArray) SetArray(array []string) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - a.array = array + a.lazyInit() + a.TArray.SetArray(array) return a } // Replace replaces the array items by given `array` from the beginning of array. func (a *StrArray) Replace(array []string) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - max := len(array) - if max > len(a.array) { - max = len(a.array) - } - for i := 0; i < max; i++ { - a.array[i] = array[i] - } + a.lazyInit() + a.TArray.Replace(array) return a } // Sum returns the sum of values in an array. func (a *StrArray) Sum() (sum int) { - a.mu.RLock() - defer a.mu.RUnlock() - for _, v := range a.array { - sum += gconv.Int(v) - } - return + a.lazyInit() + return a.TArray.Sum() } // Sort sorts the array in increasing order. // The parameter `reverse` controls whether sort // in increasing order(default) or decreasing order func (a *StrArray) Sort(reverse ...bool) *StrArray { + a.lazyInit() + a.mu.Lock() defer a.mu.Unlock() if len(reverse) > 0 && reverse[0] { @@ -147,200 +127,101 @@ func (a *StrArray) Sort(reverse ...bool) *StrArray { // SortFunc sorts the array by custom function `less`. func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - sort.Slice(a.array, func(i, j int) bool { - return less(a.array[i], a.array[j]) - }) + a.lazyInit() + a.TArray.SortFunc(less) return a } // InsertBefore inserts the `values` to the front of `index`. func (a *StrArray) InsertBefore(index int, values ...string) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - rear := append([]string{}, a.array[index:]...) - a.array = append(a.array[0:index], values...) - a.array = append(a.array, rear...) - return nil + a.lazyInit() + return a.TArray.InsertBefore(index, values...) } // InsertAfter inserts the `values` to the back of `index`. func (a *StrArray) InsertAfter(index int, values ...string) error { - a.mu.Lock() - defer a.mu.Unlock() - if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) - } - rear := append([]string{}, a.array[index+1:]...) - a.array = append(a.array[0:index+1], values...) - a.array = append(a.array, rear...) - return nil + a.lazyInit() + return a.TArray.InsertAfter(index, values...) } // Remove removes an item by index. // If the given `index` is out of range of the array, the `found` is false. func (a *StrArray) Remove(index int) (value string, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - return a.doRemoveWithoutLock(index) -} - -// doRemoveWithoutLock removes an item by index without lock. -func (a *StrArray) doRemoveWithoutLock(index int) (value string, found bool) { - if index < 0 || index >= len(a.array) { - return "", false - } - // Determine array boundaries when deleting to improve deletion efficiency. - if index == 0 { - value := a.array[0] - a.array = a.array[1:] - return value, true - } else if index == len(a.array)-1 { - value := a.array[index] - a.array = a.array[:index] - return value, true - } - // If it is a non-boundary delete, - // it will involve the creation of an array, - // then the deletion is less efficient. - value = a.array[index] - a.array = append(a.array[:index], a.array[index+1:]...) - return value, true + a.lazyInit() + return a.TArray.Remove(index) } // RemoveValue removes an item by value. // It returns true if value is found in the array, or else false if not found. func (a *StrArray) RemoveValue(value string) bool { - if i := a.Search(value); i != -1 { - _, found := a.Remove(i) - return found - } - return false + a.lazyInit() + return a.TArray.RemoveValue(value) } // RemoveValues removes multiple items by `values`. func (a *StrArray) RemoveValues(values ...string) { - a.mu.Lock() - defer a.mu.Unlock() - for _, value := range values { - if i := a.doSearchWithoutLock(value); i != -1 { - a.doRemoveWithoutLock(i) - } - } + a.lazyInit() + a.TArray.RemoveValues(values...) } // PushLeft pushes one or multiple items to the beginning of array. func (a *StrArray) PushLeft(value ...string) *StrArray { - a.mu.Lock() - a.array = append(value, a.array...) - a.mu.Unlock() + a.lazyInit() + a.TArray.PushLeft(value...) return a } // PushRight pushes one or multiple items to the end of array. // It equals to Append. func (a *StrArray) PushRight(value ...string) *StrArray { - a.mu.Lock() - a.array = append(a.array, value...) - a.mu.Unlock() + a.lazyInit() + a.TArray.PushRight(value...) return a } // PopLeft pops and returns an item from the beginning of array. // Note that if the array is empty, the `found` is false. func (a *StrArray) PopLeft() (value string, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - if len(a.array) == 0 { - return "", false - } - value = a.array[0] - a.array = a.array[1:] - return value, true + a.lazyInit() + return a.TArray.PopLeft() } // PopRight pops and returns an item from the end of array. // Note that if the array is empty, the `found` is false. func (a *StrArray) PopRight() (value string, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - index := len(a.array) - 1 - if index < 0 { - return "", false - } - value = a.array[index] - a.array = a.array[:index] - return value, true + a.lazyInit() + return a.TArray.PopRight() } // PopRand randomly pops and return an item out of array. // Note that if the array is empty, the `found` is false. func (a *StrArray) PopRand() (value string, found bool) { - a.mu.Lock() - defer a.mu.Unlock() - return a.doRemoveWithoutLock(grand.Intn(len(a.array))) + a.lazyInit() + return a.TArray.PopRand() } // PopRands randomly pops and returns `size` items out of array. // If the given `size` is greater than size of the array, it returns all elements of the array. // Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *StrArray) PopRands(size int) []string { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - if size >= len(a.array) { - size = len(a.array) - } - array := make([]string, size) - for i := 0; i < size; i++ { - array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array))) - } - return array + a.lazyInit() + return a.TArray.PopRands(size) } // PopLefts pops and returns `size` items from the beginning of array. // If the given `size` is greater than size of the array, it returns all elements of the array. // Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *StrArray) PopLefts(size int) []string { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - if size >= len(a.array) { - array := a.array - a.array = a.array[:0] - return array - } - value := a.array[0:size] - a.array = a.array[size:] - return value + a.lazyInit() + return a.TArray.PopLefts(size) } // PopRights pops and returns `size` items from the end of array. // If the given `size` is greater than size of the array, it returns all elements of the array. // Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *StrArray) PopRights(size int) []string { - a.mu.Lock() - defer a.mu.Unlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - index := len(a.array) - size - if index <= 0 { - array := a.array - a.array = a.array[:0] - return array - } - value := a.array[index:] - a.array = a.array[:index] - return value + a.lazyInit() + return a.TArray.PopRights(size) } // Range picks and returns items by range, like array[start:end]. @@ -351,26 +232,8 @@ func (a *StrArray) PopRights(size int) []string { // If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *StrArray) Range(start int, end ...int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - offsetEnd := len(a.array) - if len(end) > 0 && end[0] < offsetEnd { - offsetEnd = end[0] - } - if start > offsetEnd { - return nil - } - if start < 0 { - start = 0 - } - array := ([]string)(nil) - if a.mu.IsSafe() { - array = make([]string, offsetEnd-start) - copy(array, a.array[start:offsetEnd]) - } else { - array = a.array[start:offsetEnd] - } - return array + a.lazyInit() + return a.TArray.Range(start, end...) } // SubSlice returns a slice of elements from the array as specified @@ -387,111 +250,63 @@ func (a *StrArray) Range(start int, end ...int) []string { // // Any possibility crossing the left border of array, it will fail. func (a *StrArray) SubSlice(offset int, length ...int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - size := len(a.array) - if len(length) > 0 { - size = length[0] - } - if offset > len(a.array) { - return nil - } - if offset < 0 { - offset = len(a.array) + offset - if offset < 0 { - return nil - } - } - if size < 0 { - offset += size - size = -size - if offset < 0 { - return nil - } - } - end := offset + size - if end > len(a.array) { - end = len(a.array) - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]string, size) - copy(s, a.array[offset:]) - return s - } - return a.array[offset:end] + a.lazyInit() + return a.TArray.SubSlice(offset, length...) } // Append is alias of PushRight,please See PushRight. func (a *StrArray) Append(value ...string) *StrArray { - a.mu.Lock() - a.array = append(a.array, value...) - a.mu.Unlock() + a.lazyInit() + a.TArray.Append(value...) return a } // Len returns the length of array. func (a *StrArray) Len() int { - a.mu.RLock() - length := len(a.array) - a.mu.RUnlock() - return length + a.lazyInit() + return a.TArray.Len() } // Slice returns the underlying data of array. // Note that, if it's in concurrent-safe usage, it returns a copy of underlying data, // or else a pointer to the underlying data. func (a *StrArray) Slice() []string { - array := ([]string)(nil) - if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]string, len(a.array)) - copy(array, a.array) - } else { - array = a.array - } - return array + a.lazyInit() + return a.TArray.Slice() } // Interfaces returns current array as []any. func (a *StrArray) Interfaces() []any { - a.mu.RLock() - defer a.mu.RUnlock() - array := make([]any, len(a.array)) - for k, v := range a.array { - array[k] = v - } - return array + a.lazyInit() + return a.TArray.Interfaces() } // Clone returns a new array, which is a copy of current array. func (a *StrArray) Clone() (newArray *StrArray) { - a.mu.RLock() - array := make([]string, len(a.array)) - copy(array, a.array) - a.mu.RUnlock() - return NewStrArrayFrom(array, a.mu.IsSafe()) + a.lazyInit() + return &StrArray{ + TArray: a.TArray.Clone(), + } } // Clear deletes all items of current array. func (a *StrArray) Clear() *StrArray { - a.mu.Lock() - if len(a.array) > 0 { - a.array = make([]string, 0) - } - a.mu.Unlock() + a.lazyInit() + a.TArray.Clear() return a } // Contains checks whether a value exists in the array. func (a *StrArray) Contains(value string) bool { - return a.Search(value) != -1 + a.lazyInit() + return a.TArray.Contains(value) } // ContainsI checks whether a value exists in the array with case-insensitively. // Note that it internally iterates the whole array to do the comparison with case-insensitively. func (a *StrArray) ContainsI(value string) bool { + a.lazyInit() + a.mu.RLock() defer a.mu.RUnlock() if len(a.array) == 0 { @@ -508,64 +323,29 @@ func (a *StrArray) ContainsI(value string) bool { // Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *StrArray) Search(value string) int { - a.mu.RLock() - defer a.mu.RUnlock() - return a.doSearchWithoutLock(value) -} - -func (a *StrArray) doSearchWithoutLock(value string) int { - if len(a.array) == 0 { - return -1 - } - result := -1 - for index, v := range a.array { - if strings.Compare(v, value) == 0 { - result = index - break - } - } - return result + a.lazyInit() + return a.TArray.Search(value) } // Unique uniques the array, clear repeated items. // Example: [1,1,2,3,2] -> [1,2,3] func (a *StrArray) Unique() *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - if len(a.array) == 0 { - return a - } - var ( - ok bool - temp string - uniqueSet = make(map[string]struct{}) - uniqueArray = make([]string, 0, len(a.array)) - ) - for i := 0; i < len(a.array); i++ { - temp = a.array[i] - if _, ok = uniqueSet[temp]; ok { - continue - } - uniqueSet[temp] = struct{}{} - uniqueArray = append(uniqueArray, temp) - } - a.array = uniqueArray + a.lazyInit() + a.TArray.Unique() return a } // LockFunc locks writing by callback function `f`. func (a *StrArray) LockFunc(f func(array []string)) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - f(a.array) + a.lazyInit() + a.TArray.LockFunc(f) return a } // RLockFunc locks reading by callback function `f`. func (a *StrArray) RLockFunc(f func(array []string)) *StrArray { - a.mu.RLock() - defer a.mu.RUnlock() - f(a.array) + a.lazyInit() + a.TArray.RLockFunc(f) return a } @@ -580,42 +360,16 @@ func (a *StrArray) Merge(array any) *StrArray { // Fill fills an array with num entries of the value `value`, // keys starting at the `startIndex` parameter. func (a *StrArray) Fill(startIndex int, num int, value string) error { - a.mu.Lock() - defer a.mu.Unlock() - if startIndex < 0 || startIndex > len(a.array) { - return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) - } - for i := startIndex; i < startIndex+num; i++ { - if i > len(a.array)-1 { - a.array = append(a.array, value) - } else { - a.array[i] = value - } - } - return nil + a.lazyInit() + return a.TArray.Fill(startIndex, num, value) } // Chunk splits an array into multiple arrays, // the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *StrArray) Chunk(size int) [][]string { - if size < 1 { - return nil - } - a.mu.RLock() - defer a.mu.RUnlock() - length := len(a.array) - chunks := int(math.Ceil(float64(length) / float64(size))) - var n [][]string - for i, end := 0, 0; chunks > 0; chunks-- { - end = (i + 1) * size - if end > length { - end = length - } - n = append(n, a.array[i*size:end]) - i++ - } - return n + a.lazyInit() + return a.TArray.Chunk(size) } // Pad pads array to the specified length with `value`. @@ -623,98 +377,47 @@ func (a *StrArray) Chunk(size int) [][]string { // If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *StrArray) Pad(size int, value string) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) { - return a - } - n := size - if size < 0 { - n = -size - } - n -= len(a.array) - tmp := make([]string, n) - for i := 0; i < n; i++ { - tmp[i] = value - } - if size > 0 { - a.array = append(a.array, tmp...) - } else { - a.array = append(tmp, a.array...) - } + a.lazyInit() + a.TArray.Pad(size, value) return a } // Rand randomly returns one item from array(no deleting). func (a *StrArray) Rand() (value string, found bool) { - a.mu.RLock() - defer a.mu.RUnlock() - if len(a.array) == 0 { - return "", false - } - return a.array[grand.Intn(len(a.array))], true + a.lazyInit() + return a.TArray.Rand() } // Rands randomly returns `size` items from array(no deleting). func (a *StrArray) Rands(size int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - if size <= 0 || len(a.array) == 0 { - return nil - } - array := make([]string, size) - for i := 0; i < size; i++ { - array[i] = a.array[grand.Intn(len(a.array))] - } - return array + a.lazyInit() + return a.TArray.Rands(size) } // Shuffle randomly shuffles the array. func (a *StrArray) Shuffle() *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - for i, v := range grand.Perm(len(a.array)) { - a.array[i], a.array[v] = a.array[v], a.array[i] - } + a.lazyInit() + a.TArray.Shuffle() return a } // Reverse makes array with elements in reverse order. func (a *StrArray) Reverse() *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 { - a.array[i], a.array[j] = a.array[j], a.array[i] - } + a.lazyInit() + a.TArray.Reverse() return a } // Join joins array elements with a string `glue`. func (a *StrArray) Join(glue string) string { - a.mu.RLock() - defer a.mu.RUnlock() - if len(a.array) == 0 { - return "" - } - buffer := bytes.NewBuffer(nil) - for k, v := range a.array { - buffer.WriteString(v) - if k != len(a.array)-1 { - buffer.WriteString(glue) - } - } - return buffer.String() + a.lazyInit() + return a.TArray.Join(glue) } // CountValues counts the number of occurrences of all values in the array. func (a *StrArray) CountValues() map[string]int { - m := make(map[string]int) - a.mu.RLock() - defer a.mu.RUnlock() - for _, v := range a.array { - m[v]++ - } - return m + a.lazyInit() + return a.TArray.CountValues() } // Iterator is alias of IteratorAsc. @@ -725,25 +428,15 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) { // IteratorAsc iterates the array readonly in ascending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *StrArray) IteratorAsc(f func(k int, v string) bool) { - a.mu.RLock() - defer a.mu.RUnlock() - for k, v := range a.array { - if !f(k, v) { - break - } - } + a.lazyInit() + a.TArray.IteratorAsc(f) } // IteratorDesc iterates the array readonly in descending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *StrArray) IteratorDesc(f func(k int, v string) bool) { - a.mu.RLock() - defer a.mu.RUnlock() - for i := len(a.array) - 1; i >= 0; i-- { - if !f(i, a.array[i]) { - break - } - } + a.lazyInit() + a.TArray.IteratorDesc(f) } // String returns current array as a string, which implements like json.Marshal does. @@ -751,6 +444,9 @@ func (a *StrArray) String() string { if a == nil { return "" } + + a.lazyInit() + a.mu.RLock() defer a.mu.RUnlock() buffer := bytes.NewBuffer(nil) @@ -768,80 +464,49 @@ func (a *StrArray) String() string { // MarshalJSON implements the interface MarshalJSON for json.Marshal. // Note that do not use pointer as its receiver here. func (a StrArray) MarshalJSON() ([]byte, error) { - a.mu.RLock() - defer a.mu.RUnlock() - return json.Marshal(a.array) + a.lazyInit() + return a.TArray.MarshalJSON() } // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (a *StrArray) UnmarshalJSON(b []byte) error { - if a.array == nil { - a.array = make([]string, 0) - } - a.mu.Lock() - defer a.mu.Unlock() - if err := json.UnmarshalUseNumber(b, &a.array); err != nil { - return err - } - return nil + a.lazyInit() + return a.TArray.UnmarshalJSON(b) } // UnmarshalValue is an interface implement which sets any type of value for array. func (a *StrArray) UnmarshalValue(value any) error { - a.mu.Lock() - defer a.mu.Unlock() - switch value.(type) { - case string, []byte: - return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) - default: - a.array = gconv.SliceStr(value) - } - return nil + a.lazyInit() + return a.TArray.UnmarshalValue(value) } // Filter iterates array and filters elements using custom callback function. // It removes the element from array if callback function `filter` returns true, // it or else does nothing and continues iterating. func (a *StrArray) Filter(filter func(index int, value string) bool) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if filter(i, a.array[i]) { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.Filter(filter) return a } // FilterEmpty removes all empty string value of the array. func (a *StrArray) FilterEmpty() *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - for i := 0; i < len(a.array); { - if a.array[i] == "" { - a.array = append(a.array[:i], a.array[i+1:]...) - } else { - i++ - } - } + a.lazyInit() + a.TArray.FilterEmpty() return a } // Walk applies a user supplied function `f` to every item of array. func (a *StrArray) Walk(f func(value string) string) *StrArray { - a.mu.Lock() - defer a.mu.Unlock() - for i, v := range a.array { - a.array[i] = f(v) - } + a.lazyInit() + a.TArray.Walk(f) return a } // IsEmpty checks whether the array is empty. func (a *StrArray) IsEmpty() bool { - return a.Len() == 0 + a.lazyInit() + return a.TArray.IsEmpty() } // DeepCopy implements interface for deep copy of current type. @@ -849,9 +514,8 @@ func (a *StrArray) DeepCopy() any { if a == nil { return nil } - a.mu.RLock() - defer a.mu.RUnlock() - newSlice := make([]string, len(a.array)) - copy(newSlice, a.array) - return NewStrArrayFrom(newSlice, a.mu.IsSafe()) + a.lazyInit() + return &StrArray{ + TArray: a.TArray.DeepCopy().(*TArray[string]), + } } diff --git a/container/garray/garray_normal_t.go b/container/garray/garray_normal_t.go index d10192dc048..fa0ce054013 100644 --- a/container/garray/garray_normal_t.go +++ b/container/garray/garray_normal_t.go @@ -7,35 +7,44 @@ package garray import ( + "bytes" + "math" + "sort" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/internal/deepcopy" + "github.com/gogf/gf/v2/internal/empty" + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/internal/rwmutex" + "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/grand" ) // TArray is a golang array with rich features. // It contains a concurrent-safe/unsafe switch, which should be set // when its initialization and cannot be changed then. -// TArray is a wrapper of Array. It is designed to make using Array more convenient. type TArray[T comparable] struct { - Array + mu rwmutex.RWMutex + array []T } // NewTArray creates and returns an empty array. // The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewTArray[T comparable](safe ...bool) *TArray[T] { - return &TArray[T]{ - Array: *NewArray(safe...), - } + return NewTArraySize[T](0, 0, safe...) } // NewTArraySize create and returns an array with given size and cap. // The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewTArraySize[T comparable](size int, cap int, safe ...bool) *TArray[T] { - arr := NewArraySize(size, cap, safe...) - ret := &TArray[T]{ - Array: *arr, + return &TArray[T]{ + mu: rwmutex.Create(safe...), + array: make([]T, size, cap), } - return ret } // NewTArrayFrom creates and returns an array with given slice `array`. @@ -43,7 +52,8 @@ func NewTArraySize[T comparable](size int, cap int, safe ...bool) *TArray[T] { // which is false in default. func NewTArrayFrom[T comparable](array []T, safe ...bool) *TArray[T] { return &TArray[T]{ - Array: *NewArrayFrom(tToAnySlice(array), safe...), + mu: rwmutex.Create(safe...), + array: array, } } @@ -51,152 +61,271 @@ func NewTArrayFrom[T comparable](array []T, safe ...bool) *TArray[T] { // The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewTArrayFromCopy[T comparable](array []T, safe ...bool) *TArray[T] { + newArray := make([]T, len(array)) + copy(newArray, array) return &TArray[T]{ - Array: *NewArrayFromCopy(tToAnySlice(array), safe...), + mu: rwmutex.Create(safe...), + array: newArray, } } // At returns the value by the specified index. // If the given `index` is out of range of the array, it returns `nil`. func (a *TArray[T]) At(index int) (value T) { - value, _ = a.Array.At(index).(T) + value, _ = a.Get(index) return } // Get returns the value by the specified index. // If the given `index` is out of range of the array, the `found` is false. func (a *TArray[T]) Get(index int) (value T, found bool) { - val, found := a.Array.Get(index) - if !found { + a.mu.RLock() + defer a.mu.RUnlock() + if index < 0 || index >= len(a.array) { + found = false return } - value, _ = val.(T) - return + return a.array[index], true } // Set sets value to specified index. func (a *TArray[T]) Set(index int, value T) error { - return a.Array.Set(index, value) + a.mu.Lock() + defer a.mu.Unlock() + if index < 0 || index >= len(a.array) { + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + } + a.array[index] = value + return nil } // SetArray sets the underlying slice array with the given `array`. func (a *TArray[T]) SetArray(array []T) *TArray[T] { - a.Array.SetArray(tToAnySlice(array)) + a.mu.Lock() + defer a.mu.Unlock() + a.array = array return a } // Replace replaces the array items by given `array` from the beginning of array. func (a *TArray[T]) Replace(array []T) *TArray[T] { - a.Array.Replace(tToAnySlice(array)) + a.mu.Lock() + defer a.mu.Unlock() + max := len(array) + if max > len(a.array) { + max = len(a.array) + } + for i := 0; i < max; i++ { + a.array[i] = array[i] + } return a } // Sum returns the sum of values in an array. -func (a *TArray[T]) Sum() int { - return a.Array.Sum() +func (a *TArray[T]) Sum() (sum int) { + a.mu.RLock() + defer a.mu.RUnlock() + for _, v := range a.array { + sum += gconv.Int(v) + } + return } // SortFunc sorts the array by custom function `less`. func (a *TArray[T]) SortFunc(less func(v1, v2 T) bool) *TArray[T] { - a.Array.SortFunc(func(v1, v2 any) bool { - v1t, _ := v1.(T) - v2t, _ := v2.(T) - return less(v1t, v2t) + a.mu.Lock() + defer a.mu.Unlock() + sort.Slice(a.array, func(i, j int) bool { + return less(a.array[i], a.array[j]) }) return a } // InsertBefore inserts the `values` to the front of `index`. func (a *TArray[T]) InsertBefore(index int, values ...T) error { - return a.Array.InsertBefore(index, tToAnySlice(values)...) + a.mu.Lock() + defer a.mu.Unlock() + if index < 0 || index >= len(a.array) { + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + } + rear := append([]T{}, a.array[index:]...) + a.array = append(a.array[0:index], values...) + a.array = append(a.array, rear...) + return nil } // InsertAfter inserts the `values` to the back of `index`. func (a *TArray[T]) InsertAfter(index int, values ...T) error { - return a.Array.InsertAfter(index, tToAnySlice(values)...) + a.mu.Lock() + defer a.mu.Unlock() + if index < 0 || index >= len(a.array) { + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + } + rear := append([]T{}, a.array[index+1:]...) + a.array = append(a.array[0:index+1], values...) + a.array = append(a.array, rear...) + return nil } // Remove removes an item by index. // If the given `index` is out of range of the array, the `found` is false. func (a *TArray[T]) Remove(index int) (value T, found bool) { - val, found := a.Array.Remove(index) - if !found { + a.mu.Lock() + defer a.mu.Unlock() + return a.doRemoveWithoutLock(index) +} + +// doRemoveWithoutLock removes an item by index without lock. +func (a *TArray[T]) doRemoveWithoutLock(index int) (value T, found bool) { + if index < 0 || index >= len(a.array) { + found = false return } - value, _ = val.(T) - return + // Determine array boundaries when deleting to improve deletion efficiency. + if index == 0 { + value := a.array[0] + a.array = a.array[1:] + return value, true + } else if index == len(a.array)-1 { + value := a.array[index] + a.array = a.array[:index] + return value, true + } + // If it is a non-boundary delete, + // it will involve the creation of an array, + // then the deletion is less efficient. + value = a.array[index] + a.array = append(a.array[:index], a.array[index+1:]...) + return value, true } // RemoveValue removes an item by value. // It returns true if value is found in the array, or else false if not found. func (a *TArray[T]) RemoveValue(value T) bool { - return a.Array.RemoveValue(value) + a.mu.Lock() + defer a.mu.Unlock() + if i := a.doSearchWithoutLock(value); i != -1 { + a.doRemoveWithoutLock(i) + return true + } + return false } // RemoveValues removes multiple items by `values`. func (a *TArray[T]) RemoveValues(values ...T) { - a.Array.RemoveValues(tToAnySlice(values)...) + a.mu.Lock() + defer a.mu.Unlock() + for _, value := range values { + if i := a.doSearchWithoutLock(value); i != -1 { + a.doRemoveWithoutLock(i) + } + } } // PushLeft pushes one or multiple items to the beginning of array. func (a *TArray[T]) PushLeft(value ...T) *TArray[T] { - a.Array.PushLeft(tToAnySlice(value)...) + a.mu.Lock() + a.array = append(value, a.array...) + a.mu.Unlock() return a } // PushRight pushes one or multiple items to the end of array. // It equals to Append. func (a *TArray[T]) PushRight(value ...T) *TArray[T] { - a.Array.PushRight(tToAnySlice(value)...) + a.mu.Lock() + a.array = append(a.array, value...) + a.mu.Unlock() return a } // PopRand randomly pops and return an item out of array. // Note that if the array is empty, the `found` is false. func (a *TArray[T]) PopRand() (value T, found bool) { - val, found := a.Array.PopRand() - if !found { - return - } - value, _ = val.(T) - return + a.mu.Lock() + defer a.mu.Unlock() + return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } // PopRands randomly pops and returns `size` items out of array. func (a *TArray[T]) PopRands(size int) []T { - return anyToTSlice[T](a.Array.PopRands(size)) + a.mu.Lock() + defer a.mu.Unlock() + if size <= 0 || len(a.array) == 0 { + return nil + } + if size >= len(a.array) { + size = len(a.array) + } + array := make([]T, size) + for i := 0; i < size; i++ { + array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array))) + } + return array } // PopLeft pops and returns an item from the beginning of array. // Note that if the array is empty, the `found` is false. func (a *TArray[T]) PopLeft() (value T, found bool) { - val, found := a.Array.PopLeft() - if !found { + a.mu.Lock() + defer a.mu.Unlock() + if len(a.array) == 0 { + found = false return } - value, _ = val.(T) - return + value = a.array[0] + a.array = a.array[1:] + return value, true } // PopRight pops and returns an item from the end of array. // Note that if the array is empty, the `found` is false. func (a *TArray[T]) PopRight() (value T, found bool) { - val, found := a.Array.PopRight() - if !found { + a.mu.Lock() + defer a.mu.Unlock() + index := len(a.array) - 1 + if index < 0 { + found = false return } - value, _ = val.(T) - return + value = a.array[index] + a.array = a.array[:index] + return value, true } // PopLefts pops and returns `size` items from the beginning of array. func (a *TArray[T]) PopLefts(size int) []T { - return anyToTSlice[T](a.Array.PopLefts(size)) + a.mu.Lock() + defer a.mu.Unlock() + if size <= 0 || len(a.array) == 0 { + return nil + } + if size >= len(a.array) { + array := a.array + a.array = a.array[:0] + return array + } + value := a.array[0:size] + a.array = a.array[size:] + return value } // PopRights pops and returns `size` items from the end of array. func (a *TArray[T]) PopRights(size int) []T { - return anyToTSlice[T](a.Array.PopRights(size)) + a.mu.Lock() + defer a.mu.Unlock() + if size <= 0 || len(a.array) == 0 { + return nil + } + index := len(a.array) - size + if index <= 0 { + array := a.array + a.array = a.array[:0] + return array + } + value := a.array[index:] + a.array = a.array[:index] + return value } // Range picks and returns items by range, like array[start:end]. @@ -207,7 +336,26 @@ func (a *TArray[T]) PopRights(size int) []T { // If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *TArray[T]) Range(start int, end ...int) []T { - return anyToTSlice[T](a.Array.Range(start, end...)) + a.mu.RLock() + defer a.mu.RUnlock() + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { + return nil + } + if start < 0 { + start = 0 + } + array := ([]T)(nil) + if a.mu.IsSafe() { + array = make([]T, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) + } else { + array = a.array[start:offsetEnd] + } + return array } // SubSlice returns a slice of elements from the array as specified @@ -224,80 +372,161 @@ func (a *TArray[T]) Range(start int, end ...int) []T { // // Any possibility crossing the left border of array, it will fail. func (a *TArray[T]) SubSlice(offset int, length ...int) []T { - return anyToTSlice[T](a.Array.SubSlice(offset, length...)) + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]T, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } } // Append is alias of PushRight, please See PushRight. func (a *TArray[T]) Append(value ...T) *TArray[T] { - a.Array.Append(tToAnySlice(value)...) + a.PushRight(value...) return a } // Len returns the length of array. func (a *TArray[T]) Len() int { - return a.Array.Len() + a.mu.RLock() + length := len(a.array) + a.mu.RUnlock() + return length } // Slice returns the underlying data of array. // Note that, if it's in concurrent-safe usage, it returns a copy of underlying data, // or else a pointer to the underlying data. func (a *TArray[T]) Slice() []T { - return anyToTSlice[T](a.Array.Slice()) + if a.mu.IsSafe() { + a.mu.RLock() + defer a.mu.RUnlock() + array := make([]T, len(a.array)) + copy(array, a.array) + return array + } else { + return a.array + } } // Interfaces returns current array as []any. func (a *TArray[T]) Interfaces() []any { - return a.Array.Interfaces() + return tToAnySlice(a.Slice()) } // Clone returns a new array, which is a copy of current array. -func (a *TArray[T]) Clone() *TArray[T] { - return &TArray[T]{ - Array: *a.Array.Clone(), - } +func (a *TArray[T]) Clone() (newArray *TArray[T]) { + a.mu.RLock() + array := make([]T, len(a.array)) + copy(array, a.array) + a.mu.RUnlock() + return NewTArrayFrom(array, a.mu.IsSafe()) } // Clear deletes all items of current array. func (a *TArray[T]) Clear() *TArray[T] { - a.Array.Clear() + a.mu.Lock() + if len(a.array) > 0 { + a.array = make([]T, 0) + } + a.mu.Unlock() return a } // Contains checks whether a value exists in the array. func (a *TArray[T]) Contains(value T) bool { - return a.Array.Contains(value) + return a.Search(value) != -1 } // Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *TArray[T]) Search(value T) int { - return a.Array.Search(value) + a.mu.RLock() + defer a.mu.RUnlock() + return a.doSearchWithoutLock(value) +} + +func (a *TArray[T]) doSearchWithoutLock(value T) int { + if len(a.array) == 0 { + return -1 + } + result := -1 + for index, v := range a.array { + if v == value { + result = index + break + } + } + return result } // Unique uniques the array, clear repeated items. // Example: [1,1,2,3,2] -> [1,2,3] func (a *TArray[T]) Unique() *TArray[T] { - a.Array.Unique() + a.mu.Lock() + defer a.mu.Unlock() + if len(a.array) == 0 { + return a + } + var ( + ok bool + temp T + uniqueSet = make(map[T]struct{}) + uniqueArray = make([]T, 0, len(a.array)) + ) + for i := 0; i < len(a.array); i++ { + temp = a.array[i] + if _, ok = uniqueSet[temp]; ok { + continue + } + uniqueSet[temp] = struct{}{} + uniqueArray = append(uniqueArray, temp) + } + a.array = uniqueArray return a } // LockFunc locks writing by callback function `f`. func (a *TArray[T]) LockFunc(f func(array []T)) *TArray[T] { - a.Array.LockFunc(func(array []any) { - vals := anyToTSlice[T](array) - f(vals) - for k, v := range vals { - array[k] = v - } - }) + a.mu.Lock() + defer a.mu.Unlock() + f(a.array) return a } // RLockFunc locks reading by callback function `f`. func (a *TArray[T]) RLockFunc(f func(array []T)) *TArray[T] { - a.Array.RLockFunc(func(array []any) { - f(anyToTSlice[T](array)) - }) + a.mu.RLock() + defer a.mu.RUnlock() + f(a.array) return a } @@ -306,40 +535,63 @@ func (a *TArray[T]) RLockFunc(f func(array []T)) *TArray[T] { // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *TArray[T]) Merge(array any) *TArray[T] { + var vals []T switch v := array.(type) { - case *Array: - return a.Merge(v.Slice()) - case *StrArray: - return a.Merge(v.Slice()) - case *IntArray: - return a.Merge(v.Slice()) + case *SortedTArray[T]: + vals = v.Slice() case *TArray[T]: - a.Array.Merge(&v.Array) + vals = v.Slice() case []T: - a.Array.Merge(v) - case TArray[T]: - a.Array.Merge(&v.Array) + vals = v default: - var vals []T - if err := gconv.Scan(v, &vals); err != nil { + interfaces := gconv.Interfaces(v) + if err := gconv.Scan(interfaces, &vals); err != nil { panic(err) } - a.Append(vals...) } - return a + + return a.Append(vals...) } // Fill fills an array with num entries of the value `value`, // keys starting at the `startIndex` parameter. func (a *TArray[T]) Fill(startIndex int, num int, value T) error { - return a.Array.Fill(startIndex, num, value) + a.mu.Lock() + defer a.mu.Unlock() + if startIndex < 0 || startIndex > len(a.array) { + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) + } + for i := startIndex; i < startIndex+num; i++ { + if i > len(a.array)-1 { + a.array = append(a.array, value) + } else { + a.array[i] = value + } + } + return nil } // Chunk splits an array into multiple arrays, // the size of each array is determined by `size`. // The last chunk may contain less than size elements. -func (a *TArray[T]) Chunk(size int) (values [][]T) { - return anyToTSlices[T](a.Array.Chunk(size)) +func (a *TArray[T]) Chunk(size int) [][]T { + if size < 1 { + return nil + } + a.mu.RLock() + defer a.mu.RUnlock() + length := len(a.array) + chunks := int(math.Ceil(float64(length) / float64(size))) + var n [][]T + for i, end := 0, 0; chunks > 0; chunks-- { + end = (i + 1) * size + if end > length { + end = length + } + n = append(n, a.array[i*size:end]) + i++ + } + return n } // Pad pads array to the specified length with `value`. @@ -347,76 +599,128 @@ func (a *TArray[T]) Chunk(size int) (values [][]T) { // If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *TArray[T]) Pad(size int, val T) *TArray[T] { - a.Array.Pad(size, val) + a.mu.Lock() + defer a.mu.Unlock() + if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) { + return a + } + n := size + if size < 0 { + n = -size + } + n -= len(a.array) + tmp := make([]T, n) + for i := 0; i < n; i++ { + tmp[i] = val + } + if size > 0 { + a.array = append(a.array, tmp...) + } else { + a.array = append(tmp, a.array...) + } return a } // Rand randomly returns one item from array(no deleting). func (a *TArray[T]) Rand() (value T, found bool) { - val, found := a.Array.Rand() - if !found { + a.mu.RLock() + defer a.mu.RUnlock() + if len(a.array) == 0 { + found = false return } - value, _ = val.(T) - return + return a.array[grand.Intn(len(a.array))], true } // Rands randomly returns `size` items from array(no deleting). func (a *TArray[T]) Rands(size int) []T { - return anyToTSlice[T](a.Array.Rands(size)) + a.mu.RLock() + defer a.mu.RUnlock() + if size <= 0 || len(a.array) == 0 { + return nil + } + array := make([]T, size) + for i := 0; i < size; i++ { + array[i] = a.array[grand.Intn(len(a.array))] + } + return array } // Shuffle randomly shuffles the array. func (a *TArray[T]) Shuffle() *TArray[T] { - a.Array.Shuffle() + a.mu.Lock() + defer a.mu.Unlock() + for i, v := range grand.Perm(len(a.array)) { + a.array[i], a.array[v] = a.array[v], a.array[i] + } return a } // Reverse makes array with elements in reverse order. func (a *TArray[T]) Reverse() *TArray[T] { - a.Array.Reverse() + a.mu.Lock() + defer a.mu.Unlock() + for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 { + a.array[i], a.array[j] = a.array[j], a.array[i] + } return a } // Join joins array elements with a string `glue`. func (a *TArray[T]) Join(glue string) string { - return a.Array.Join(glue) + a.mu.RLock() + defer a.mu.RUnlock() + if len(a.array) == 0 { + return "" + } + buffer := bytes.NewBuffer(nil) + for k, v := range a.array { + buffer.WriteString(gconv.String(v)) + if k != len(a.array)-1 { + buffer.WriteString(glue) + } + } + return buffer.String() } // CountValues counts the number of occurrences of all values in the array. -func (a *TArray[T]) CountValues() (valueCnt map[T]int) { - valueCnt = map[T]int{} - for k, v := range a.Array.CountValues() { - k0, _ := k.(T) - valueCnt[k0] = v +func (a *TArray[T]) CountValues() map[T]int { + m := make(map[T]int) + a.mu.RLock() + defer a.mu.RUnlock() + for _, v := range a.array { + m[v]++ } - return + return m } // Iterator is alias of IteratorAsc. func (a *TArray[T]) Iterator(f func(k int, v T) bool) { - a.Array.Iterator(func(k int, v any) bool { - v0, _ := v.(T) - return f(k, v0) - }) + a.IteratorAsc(f) } // IteratorAsc iterates the array readonly in ascending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *TArray[T]) IteratorAsc(f func(k int, v T) bool) { - a.Array.IteratorAsc(func(k int, v any) bool { - v0, _ := v.(T) - return f(k, v0) - }) + a.mu.RLock() + defer a.mu.RUnlock() + for k, v := range a.array { + if !f(k, v) { + break + } + } } // IteratorDesc iterates the array readonly in descending order with given callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (a *TArray[T]) IteratorDesc(f func(k int, v T) bool) { - a.Array.IteratorDesc(func(k int, v any) bool { - v0, _ := v.(T) - return f(k, v0) - }) + a.mu.RLock() + defer a.mu.RUnlock() + for i := len(a.array) - 1; i >= 0; i-- { + if !f(i, a.array[i]) { + break + } + } } // String returns current array as a string, which implements like json.Marshal does. @@ -424,61 +728,120 @@ func (a *TArray[T]) String() string { if a == nil { return "" } - return a.Array.String() + a.mu.RLock() + defer a.mu.RUnlock() + buffer := bytes.NewBuffer(nil) + buffer.WriteByte('[') + s := "" + for k, v := range a.array { + s = gconv.String(v) + if gstr.IsNumeric(s) { + buffer.WriteString(s) + } else { + buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`) + } + if k != len(a.array)-1 { + buffer.WriteByte(',') + } + } + buffer.WriteByte(']') + return buffer.String() } // MarshalJSON implements the interface MarshalJSON for json.Marshal. // Note that do not use pointer as its receiver here. func (a TArray[T]) MarshalJSON() ([]byte, error) { - return a.Array.MarshalJSON() + a.mu.RLock() + defer a.mu.RUnlock() + return json.Marshal(a.array) } // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (a *TArray[T]) UnmarshalJSON(b []byte) error { - return a.Array.UnmarshalJSON(b) + if a.array == nil { + a.array = make([]T, 0) + } + a.mu.Lock() + defer a.mu.Unlock() + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { + return err + } + return nil } // UnmarshalValue is an interface implement which sets any type of value for array. func (a *TArray[T]) UnmarshalValue(value any) error { - return a.Array.UnmarshalValue(value) + a.mu.Lock() + defer a.mu.Unlock() + switch value.(type) { + case string, []byte: + return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) + default: + if err := gconv.Scan(gconv.SliceAny(value), &a.array); err != nil { + return err + } + } + return nil } // Filter iterates array and filters elements using custom callback function. // It removes the element from array if callback function `filter` returns true, // it or else does nothing and continues iterating. func (a *TArray[T]) Filter(filter func(index int, value T) bool) *TArray[T] { - a.Array.Filter(func(index int, value any) bool { - val, _ := value.(T) - return filter(index, val) - }) + a.mu.Lock() + defer a.mu.Unlock() + for i := 0; i < len(a.array); { + if filter(i, a.array[i]) { + a.array = append(a.array[:i], a.array[i+1:]...) + } else { + i++ + } + } return a } // FilterNil removes all nil value of the array. func (a *TArray[T]) FilterNil() *TArray[T] { - a.Array.FilterNil() + a.mu.Lock() + defer a.mu.Unlock() + for i := 0; i < len(a.array); { + if empty.IsNil(a.array[i]) { + a.array = append(a.array[:i], a.array[i+1:]...) + } else { + i++ + } + } return a } // FilterEmpty removes all empty value of the array. // Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty. func (a *TArray[T]) FilterEmpty() *TArray[T] { - a.Array.FilterEmpty() + a.mu.Lock() + defer a.mu.Unlock() + for i := 0; i < len(a.array); { + if empty.IsEmpty(a.array[i]) { + a.array = append(a.array[:i], a.array[i+1:]...) + } else { + i++ + } + } return a } // Walk applies a user supplied function `f` to every item of array. func (a *TArray[T]) Walk(f func(value T) T) *TArray[T] { - a.Array.Walk(func(value any) any { - val, _ := value.(T) - return f(val) - }) + a.mu.Lock() + defer a.mu.Unlock() + for i, v := range a.array { + a.array[i] = f(v) + } return a } // IsEmpty checks whether the array is empty. func (a *TArray[T]) IsEmpty() bool { - return a.Array.IsEmpty() + return a.Len() == 0 } // DeepCopy implements interface for deep copy of current type. @@ -486,8 +849,11 @@ func (a *TArray[T]) DeepCopy() any { if a == nil { return nil } - arr := a.Array.DeepCopy().(*Array) - return &TArray[T]{ - Array: *arr, + a.mu.RLock() + defer a.mu.RUnlock() + newSlice := make([]T, len(a.array)) + for i, v := range a.array { + newSlice[i] = deepcopy.Copy(v).(T) } + return NewTArrayFrom(newSlice, a.mu.IsSafe()) } diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index 49388bba925..00029bff061 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -9,6 +9,7 @@ package garray import ( "fmt" "sort" + "sync" "github.com/gogf/gf/v2/util/gconv" ) @@ -20,13 +21,16 @@ import ( // when its initialization and cannot be changed then. type SortedArray struct { *SortedTArray[any] + once sync.Once } // lazyInit lazily initializes the array. func (a *SortedArray) lazyInit() { - if a.SortedTArray == nil { - a.SortedTArray = NewSortedTArraySize[any](0, nil, false) - } + a.once.Do(func() { + if a.SortedTArray == nil { + a.SortedTArray = NewSortedTArraySize[any](0, nil, false) + } + }) } // NewSortedArray creates and returns an empty sorted array. diff --git a/container/garray/garray_sorted_int.go b/container/garray/garray_sorted_int.go index c9897bd0d59..a4a27a06e88 100644 --- a/container/garray/garray_sorted_int.go +++ b/container/garray/garray_sorted_int.go @@ -8,6 +8,7 @@ package garray import ( "fmt" + "sync" "github.com/gogf/gf/v2/util/gconv" ) @@ -19,14 +20,17 @@ import ( // when its initialization and cannot be changed then. type SortedIntArray struct { *SortedTArray[int] + once sync.Once } // lazyInit lazily initializes the array. func (a *SortedIntArray) lazyInit() { - if a.SortedTArray == nil { - a.SortedTArray = NewSortedTArraySize(0, defaultComparatorInt, false) - a.SetSorter(quickSortInt) - } + a.once.Do(func() { + if a.SortedTArray == nil { + a.SortedTArray = NewSortedTArraySize(0, defaultComparatorInt, false) + a.SetSorter(quickSortInt) + } + }) } // NewSortedIntArray creates and returns an empty sorted array. diff --git a/container/garray/garray_sorted_str.go b/container/garray/garray_sorted_str.go index b58d5a2ac90..e4d57188ec8 100644 --- a/container/garray/garray_sorted_str.go +++ b/container/garray/garray_sorted_str.go @@ -9,6 +9,7 @@ package garray import ( "bytes" "strings" + "sync" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" @@ -21,14 +22,17 @@ import ( // when its initialization and cannot be changed then. type SortedStrArray struct { *SortedTArray[string] + once sync.Once } // lazyInit lazily initializes the array. func (a *SortedStrArray) lazyInit() { - if a.SortedTArray == nil { - a.SortedTArray = NewSortedTArraySize(0, defaultComparatorStr, false) - a.SetSorter(quickSortStr) - } + a.once.Do(func() { + if a.SortedTArray == nil { + a.SortedTArray = NewSortedTArraySize(0, defaultComparatorStr, false) + a.SetSorter(quickSortStr) + } + }) } // NewSortedStrArray creates and returns an empty sorted array. diff --git a/container/garray/garray_z_unit_normal_t_test.go b/container/garray/garray_z_unit_normal_t_test.go index e3dbeb2a3a6..4b2032dfba6 100644 --- a/container/garray/garray_z_unit_normal_t_test.go +++ b/container/garray/garray_z_unit_normal_t_test.go @@ -63,14 +63,17 @@ func Test_TArray_Basic(t *testing.T) { v, ok = array.Remove(0) // 1, 2, 3 t.Assert(v, 100) t.Assert(ok, true) + t.Assert(array.Slice(), []int{1, 2, 3}) v, ok = array.Remove(-1) t.Assert(v, 0) t.Assert(ok, false) + t.Assert(array.Slice(), []int{1, 2, 3}) v, ok = array.Remove(100000) t.Assert(v, 0) t.Assert(ok, false) + t.Assert(array.Slice(), []int{1, 2, 3}) v, ok = array2.Remove(3) // 0 1 2 t.Assert(v, 3) @@ -81,14 +84,16 @@ func Test_TArray_Basic(t *testing.T) { t.Assert(ok, true) t.Assert(array.Contains(100), false) - array.Append(4) // 1, 2, 3 ,4 + array.Append(4) // 2, 2, 3, 4 + t.Assert(array.Slice(), []int{2, 2, 3, 4}) t.Assert(array.Len(), 4) - array.InsertBefore(0, 100) // 100, 1, 2, 3, 4 - array.InsertAfter(0, 200) // 100, 200, 1, 2, 3, 4 - t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4}) + array.InsertBefore(0, 100) // 100, 2, 2, 3, 4 + t.Assert(array.Slice(), []int{100, 2, 2, 3, 4}) + array.InsertAfter(0, 200) // 100, 200, 2, 2, 3, 4 + t.Assert(array.Slice(), []int{100, 200, 2, 2, 3, 4}) array.InsertBefore(5, 300) array.InsertAfter(6, 400) - t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400}) + t.Assert(array.Slice(), []int{100, 200, 2, 2, 3, 300, 4, 400}) t.Assert(array.Clear().Len(), 0) err = array.InsertBefore(99, 9900) t.AssertNE(err, nil) @@ -588,15 +593,15 @@ func TestTArray_RLockFunc(t *testing.T) { ch1 := make(chan int64, 3) ch2 := make(chan int64, 1) // go1 - go a1.RLockFunc(func(n1 []any) { // 读锁 - time.Sleep(2 * time.Second) // 暂停1秒 + go a1.RLockFunc(func(n1 []any) { // read lock + time.Sleep(2 * time.Second) // sleep 2 s n1[2] = "g" ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) }) // go2 go func() { - time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后,再开始执行. + time.Sleep(100 * time.Millisecond) // wait go1 do line lock for 0.01s. Then do. ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) a1.Len() ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) @@ -604,11 +609,11 @@ func TestTArray_RLockFunc(t *testing.T) { t1 := <-ch1 t2 := <-ch1 - <-ch2 // 等待go1完成 + <-ch2 // wait for go1 done. - // 防止ci抖动,以豪秒为单位 - t.AssertLT(t2-t1, 20) // go1加的读锁,所go2读的时候,并没有阻塞。 - t.Assert(a1.Contains("g"), false) + // Prevent CI jitter, in milliseconds. + t.AssertLT(t2-t1, 20) // Go1 acquired a read lock, so when Go2 reads, it is not blocked. + t.Assert(a1.Contains("g"), true) }) }