深入浅出 GoFrame:garray 从入门到实战的完整指南

116 阅读7分钟

garray是GoFrame框架提供的数组容器,支持多种数据类型的并发安全/非并发安全数组操作。本文将详细介绍garray的主要功能和使用方法。

数组类型

garray提供以下几种数组类型:

  1. garray.Array - 通用类型数组,可存储任意类型

  2. garray.IntArray - 整型数组

  3. garray.StrArray - 字符串数组

  4. garray.SortedArray - 排序数组

  5. garray.SortedIntArray - 排序整型数组

  6. 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]
}

注意事项

  1. 并发安全的数组操作会有一定的性能开销,如果确定不需要并发安全,可以创建非并发安全的数组。

  2. 排序数组的Add操作会自动维护数组顺序,性能比普通数组略低。

  3. 对于大量数据的操作,建议使用批量方法(如BatchAppend)来提高性能。

  4. 使用Iterator方法遍历数组比直接使用for循环更安全,特别是在并发环境下。

最佳实践

  1. 选择合适的数组类型:

    • 需要并发安全时使用safe模式
    • 需要保持顺序时使用排序数组
    • 对性能要求高时使用非并发安全模式
  2. 使用适当的方法:

    • 批量操作使用Batch相关方法
    • 遍历推荐使用Iterator方法
    • 需要截取数据时使用SubSlice而不是直接操作底层切片
  3. 错误处理:

    • 访问元素时总是检查返回的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)

实战应用建议

  1. 在 Web 应用中使用 garray:

    • 用于管理在线用户列表
    • 实现请求限流队列
    • 缓存最近的请求或响应
  2. 在微服务中使用 garray:

    • 实现服务节点列表管理
    • 负载均衡器的后端列表
    • 请求重试队列
  3. 在数据处理中使用 garray:

    • 数据批处理队列
    • 实时数据流缓冲
    • 多级数据过滤

总结

garray 提供了丰富的数组操作功能,既可以保证并发安全,又可以满足各种数据类型的需求。通过合理使用 garray,可以大大简化我们的数组操作代码,提高开发效率。在实际使用中,应根据具体场景选择合适的数组类型和操作方法,既要保证功能的正确性,也要考虑性能的影响。