feat(container/garray): enhance generic array implements (#4482)

Remove the t array of wrapper array. Now it's a real one. Other normal
array will base on it.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Hunk Zhu
2025-11-19 18:11:04 +08:00
committed by GitHub
parent 362d4202c4
commit 9018a3d4ac
8 changed files with 923 additions and 1601 deletions

View File

@ -7,28 +7,18 @@
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/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"
)
// 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())
}

View File

@ -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]),
}
}

View File

@ -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]),
}
}

View File

@ -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())
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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)
})
}