garray是GoFrame框架提供的数组容器,支持多种数据类型的并发安全/非并发安全数组操作。本文将详细介绍garray的主要功能和使用方法。
数组类型
garray提供以下几种数组类型:
-
garray.Array- 通用类型数组,可存储任意类型 -
garray.IntArray- 整型数组 -
garray.StrArray- 字符串数组 -
garray.SortedArray- 排序数组 -
garray.SortedIntArray- 排序整型数组 -
garray.SortedStrArray- 排序字符串数组
创建数组
1. 创建普通数组
// 创建并发安全的数组
array := garray.New(true)
// 创建非并发安全的数组
array := garray.New(false)
// 创建指定类型的数组
intArray := garray.NewIntArray(true)
strArray := garray.NewStrArray(true)
2. 创建排序数组
// 创建排序数组
sorted := garray.NewSortedArray(func(v1, v2 interface{}) int {
return strings.Compare(v1.(string), v2.(string))
})
// 创建排序整型数组
sortedInt := garray.NewSortedIntArray(true)
// 创建排序字符串数组
sortedStr := garray.NewSortedStrArray(true)
基本操作
1. 添加元素
// 在数组末尾追加元素
array.Append("hello")
array.Append(1, 2, 3)
// 批量追加元素
array.Append([]interface{}{4, 5, 6}...)
2. 删除元素
// 删除指定位置的元素
array.Remove(0)
// 删除指定值的元素
array.RemoveValue("hello")
// 清空数组
array.Clear()
3. 查询和获取元素
// 获取指定位置的元素
value, found := array.Get(0)
// 查找元素位置
index := array.Search("hello")
// 判断元素是否存在
exists := array.Contains("world")
// 获取数组长度
length := array.Len()
高级特性
1. 数组遍历
// 使用Iterator方法遍历
array.Iterator(func(k int, v interface{}) bool {
fmt.Printf("index:%d, value:%v\n", k, v)
return true
})
// 使用IteratorAsc方法正序遍历
array.IteratorAsc(func(k int, v interface{}) bool {
return true
})
// 使用IteratorDesc方法倒序遍历
array.IteratorDesc(func(k int, v interface{}) bool {
return true
})
2. 数组排序
// 就地排序
array.Sort()
// 自定义排序
array.SetComparator(func(v1, v2 interface{}) int {
return v1.(int) - v2.(int)
})
3. 数组截取和复制
// 截取子数组
subArray := array.SubSlice(1, 3)
// 复制数组
newArray := array.Clone()
并发安全示例
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"sync"
)
func main() {
// 创建并发安全的整型数组
array := garray.NewIntArray(true)
// 模拟并发写入
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
array.Append(n)
}(i)
}
wg.Wait()
// 打印结果
fmt.Println(array.Slice())
}
排序数组示例
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
)
func main() {
// 创建排序字符串数组
array := garray.NewSortedStrArray(true)
// 添加元素(自动排序)
array.Add("z")
array.Add("a")
array.Add("b")
// 打印结果(已排序)
fmt.Println(array.Slice()) // 输出: [a b z]
}
注意事项
-
并发安全的数组操作会有一定的性能开销,如果确定不需要并发安全,可以创建非并发安全的数组。
-
排序数组的Add操作会自动维护数组顺序,性能比普通数组略低。
-
对于大量数据的操作,建议使用批量方法(如BatchAppend)来提高性能。
-
使用Iterator方法遍历数组比直接使用for循环更安全,特别是在并发环境下。
最佳实践
-
选择合适的数组类型:
- 需要并发安全时使用safe模式
- 需要保持顺序时使用排序数组
- 对性能要求高时使用非并发安全模式
-
使用适当的方法:
- 批量操作使用Batch相关方法
- 遍历推荐使用Iterator方法
- 需要截取数据时使用SubSlice而不是直接操作底层切片
-
错误处理:
- 访问元素时总是检查返回的bool值
- 对于可能的越界操作要做好处理
高级应用场景
1. 实现简单的任务队列
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"time"
)
type Task struct {
ID int
Content string
}
func main() {
// 创建任务队列
queue := garray.NewArray(true)
// 生产者
go func() {
for i := 0; i < 10; i++ {
task := Task{
ID: i,
Content: fmt.Sprintf("Task-%d", i),
}
queue.Append(task)
time.Sleep(time.Second)
}
}()
// 消费者
go func() {
for {
if queue.Len() > 0 {
if v, ok := queue.Remove(0); ok {
task := v.(Task)
fmt.Printf("Processing task: %+v\n", task)
}
}
time.Sleep(time.Second * 2)
}
}()
// 等待程序运行
select {}
}
2. 实现数据缓存
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"sync"
"time"
)
type Cache struct {
data *garray.Array
limit int
mu sync.RWMutex
}
func NewCache(limit int) *Cache {
return &Cache{
data: garray.New(true),
limit: limit,
}
}
func (c *Cache) Add(value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
if c.data.Len() >= c.limit {
c.data.Remove(0)
}
c.data.Append(value)
}
func (c *Cache) Get(index int) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
return c.data.Get(index)
}
func main() {
cache := NewCache(5)
// 添加数据
for i := 0; i < 10; i++ {
cache.Add(i)
fmt.Printf("After adding %d: %v\n", i, cache.data.Slice())
time.Sleep(time.Second)
}
}
3. 实现优先级队列
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
)
type PriorityItem struct {
Value interface{}
Priority int
}
type PriorityQueue struct {
array *garray.SortedArray
}
func NewPriorityQueue() *PriorityQueue {
return &PriorityQueue{
array: garray.NewSortedArray(func(v1, v2 interface{}) int {
p1 := v1.(PriorityItem).Priority
p2 := v2.(PriorityItem).Priority
// 优先级高的排在前面
return p2 - p1
}),
}
}
func (pq *PriorityQueue) Push(value interface{}, priority int) {
pq.array.Add(PriorityItem{
Value: value,
Priority: priority,
})
}
func (pq *PriorityQueue) Pop() (interface{}, bool) {
if pq.array.Len() == 0 {
return nil, false
}
if v, ok := pq.array.Remove(0); ok {
return v.(PriorityItem).Value, true
}
return nil, false
}
func main() {
pq := NewPriorityQueue()
// 添加不同优先级的任务
pq.Push("低优先级任务", 1)
pq.Push("高优先级任务", 3)
pq.Push("中优先级任务", 2)
// 按优先级处理任务
for {
if v, ok := pq.Pop(); ok {
fmt.Printf("Processing: %v\n", v)
} else {
break
}
}
}
4. 数据去重和过滤
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
)
func main() {
// 创建字符串数组
array := garray.NewStrArray(true)
// 添加重复数据
array.Append("a", "b", "a", "c", "b", "d")
// 去重
unique := garray.NewStrArray(true)
array.Iterator(func(k int, v string) bool {
if !unique.Contains(v) {
unique.Append(v)
}
return true
})
fmt.Println("Original:", array.Slice())
fmt.Println("Unique:", unique.Slice())
// 过滤数据
filtered := array.FilterEmpty()
fmt.Println("Filtered:", filtered.Slice())
}
实战技巧
1. 内存管理优化
1.1 内存复用策略
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
)
type ObjectPool struct {
pool *garray.Array
}
func NewObjectPool(size int) *ObjectPool {
pool := &ObjectPool{
pool: garray.NewArray(true),
}
// 预分配对象
for i := 0; i < size; i++ {
pool.pool.Append(new(MyObject))
}
return pool
}
type MyObject struct {
data []byte
}
func (p *ObjectPool) Get() (*MyObject, bool) {
if v, ok := p.pool.Remove(0); ok {
return v.(*MyObject), true
}
return nil, false
}
func (p *ObjectPool) Put(obj *MyObject) {
// 清理对象数据
obj.data = obj.data[:0]
p.pool.Append(obj)
}
func main() {
pool := NewObjectPool(10)
// 使用对象
obj, _ := pool.Get()
obj.data = append(obj.data, []byte("test data")...)
// 归还对象
pool.Put(obj)
}
1.2 大数据集处理
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"runtime"
)
func processBigData(data []int) {
// 使用固定大小的窗口处理数据
window := garray.NewIntArray(true)
windowSize := 1000
for _, item := range data {
window.Append(item)
if window.Len() >= windowSize {
// 处理窗口数据
processWindow(window)
// 清空窗口
window.Clear()
// 触发GC
runtime.GC()
}
}
// 处理剩余数据
if window.Len() > 0 {
processWindow(window)
}
}
func processWindow(window *garray.IntArray) {
// 处理逻辑
window.Iterator(func(k int, v int) bool {
// 处理每个元素
return true
})
}
2. 高级并发控制
2.1 带超时的并发操作
package main
import (
"context"
"fmt"
"github.com/gogf/gf/v2/container/garray"
"time"
)
type ConcurrentQueue struct {
array *garray.Array
}
func NewConcurrentQueue() *ConcurrentQueue {
return &ConcurrentQueue{
array: garray.New(true),
}
}
func (q *ConcurrentQueue) PopWithTimeout(ctx context.Context) (interface{}, error) {
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
if q.array.Len() > 0 {
if v, ok := q.array.Remove(0); ok {
return v, nil
}
}
time.Sleep(time.Millisecond * 100)
}
}
}
func main() {
queue := NewConcurrentQueue()
// 添加测试数据
go func() {
time.Sleep(time.Second * 2)
queue.array.Append("test data")
}()
// 设置超时时间
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
// 尝试获取数据
if v, err := queue.PopWithTimeout(ctx); err != nil {
fmt.Println("Timeout:", err)
} else {
fmt.Println("Got value:", v)
}
}
2.2 条件等待机制
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"sync"
"time"
)
type WaitQueue struct {
array *garray.Array
cond *sync.Cond
}
func NewWaitQueue() *WaitQueue {
return &WaitQueue{
array: garray.New(true),
cond: sync.NewCond(&sync.Mutex{}),
}
}
func (q *WaitQueue) Push(v interface{}) {
q.array.Append(v)
q.cond.Signal() // 通知等待的消费者
}
func (q *WaitQueue) Pop() interface{} {
q.cond.L.Lock()
defer q.cond.L.Unlock()
for q.array.Len() == 0 {
q.cond.Wait()
}
v, _ := q.array.Remove(0)
return v
}
func main() {
queue := NewWaitQueue()
// 消费者
go func() {
for {
v := queue.Pop()
fmt.Println("Consumed:", v)
}
}()
// 生产者
for i := 0; i < 5; i++ {
queue.Push(i)
time.Sleep(time.Second)
}
}
3. 自定义序列化
package main
import (
"encoding/json"
"fmt"
"github.com/gogf/gf/v2/container/garray"
)
type CustomArray struct {
*garray.Array
}
func (ca *CustomArray) MarshalJSON() ([]byte, error) {
return json.Marshal(ca.Array.Slice())
}
func (ca *CustomArray) UnmarshalJSON(data []byte) error {
var slice []interface{}
if err := json.Unmarshal(data, &slice); err != nil {
return err
}
ca.Array = garray.NewArrayFrom(slice, true)
return nil
}
func main() {
// 创建自定义数组
arr := &CustomArray{Array: garray.NewArrayFrom([]interface{}{1, 2, 3}, true)}
// 序列化
data, _ := json.Marshal(arr)
fmt.Println("JSON:", string(data))
// 反序列化
newArr := &CustomArray{}
json.Unmarshal(data, newArr)
fmt.Println("Restored:", newArr.Slice())
}
4. 异常处理和恢复机制
package main
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"time"
)
type SafeArray struct {
array *garray.Array
backup *garray.Array
}
func NewSafeArray() *SafeArray {
return &SafeArray{
array: garray.New(true),
backup: garray.New(true),
}
}
func (sa *SafeArray) SafeAppend(value interface{}) {
// 备份当前状态
sa.backup = sa.array.Clone()
defer func() {
if err := recover(); err != nil {
// 发生异常时恢复到之前的状态
sa.array = sa.backup
fmt.Printf("Recovered from error: %v\n", err)
}
}()
sa.array.Append(value)
}
func main() {
sa := NewSafeArray()
// 正常操作
sa.SafeAppend("test1")
fmt.Println("Current:", sa.array.Slice())
// 模拟异常操作
sa.SafeAppend(nil)
fmt.Println("After recovery:", sa.array.Slice())
}
这些实战技巧可以帮助我们在实际项目中更好地使用 garray。
性能优化建议
1. 预分配容量
// 当知道数据量时,预分配容量可以提高性能
array := garray.NewIntArraySize(0, 1000)
2. 批量操作
// 使用批量追加而不是循环追加
array.Append(slice...)
3. 合理使用锁
// 读多写少的场景使用RWMutex
array.LockFunc(func(a []interface{}) {
// 写操作
})
array.RLockFunc(func(a []interface{}) {
// 读操作
})
4. 避免频繁的类型转换
// 使用特定类型的数组可以避免类型转换
intArray := garray.NewIntArray(true)
strArray := garray.NewStrArray(true)
实战应用建议
-
在 Web 应用中使用 garray:
- 用于管理在线用户列表
- 实现请求限流队列
- 缓存最近的请求或响应
-
在微服务中使用 garray:
- 实现服务节点列表管理
- 负载均衡器的后端列表
- 请求重试队列
-
在数据处理中使用 garray:
- 数据批处理队列
- 实时数据流缓冲
- 多级数据过滤
总结
garray 提供了丰富的数组操作功能,既可以保证并发安全,又可以满足各种数据类型的需求。通过合理使用 garray,可以大大简化我们的数组操作代码,提高开发效率。在实际使用中,应根据具体场景选择合适的数组类型和操作方法,既要保证功能的正确性,也要考虑性能的影响。