Skip to content

为什么我觉得GoFrame的garray比PHP的array还好用?

前言

写过PHP的同学都知道 PHP的数组Array非常好用,特别灵活

我在写PHP之前使用Java做安卓开发,在接触PHP的数组Array之后,直呼太香了!

而在学习Go基础知识的时候了解到Go的数组和PHP的数组并不一样;从一定程度上讲,Go的slice切片类型和PHP的数组array比较像(不固定长度、引用类型、动态扩容等),但是在开发使用中远远不像PHP的array灵活。

初识GoFrame

最近在使用基于Go语言的GoFrame框架撸项目,发现GoFrame封装的garray竟然比PHP的array还要好用。

近期已经更新了一系列GoFrame的文章,下文将GoFrame简称为gf。感兴趣的同学可以关注我的专栏:Go语言学习专栏 ,目前已经更新了120多篇原创文章,获得了700多位同学的关注。

gf框架有个特点,提供的组件基本都支持设置并发安全开关。显然PHP是不支持并发安全开关的,PHP的数组是并发安全的。PHP-FPM是阻塞的单线程模型,PHP-FPM每个进程里只有一个线程,一个进程同时只能服务一个客户端。

garray特点简介

  1. garray支持int/string/interface{}三种常用的数据类型。

  2. garray支持普通数组和排序数组,普通数组的结构体名称定义为_Array格式,排序数组的结构体名称定义为Sorted_Array格式,如下:Array, intArray, StrArray,SortedArray, SortedIntArray, SortedStrArray

  3. 其中排序数组SortedArray,需要给定排序比较方法,在工具包gutil中也定义了很多ComparatorXXX的比较方法,用起来很方便。当然也支持自定义排序方式。

基本使用

go
package main import (    "fmt"    "github.com/gogf/gf/container/garray" )
func main() {
//创建并发安全的int型数组
a := garray.NewIntArray(true)
//添加数组项
for i := 0;
i < 10;
i++ {       a.Append(i)
}
// 打印结果:    fmt.Println(a)
//"[0,1,2,3,4,5,6,7,8,9]"    fmt.Println("数组长度:", a.Len())    fmt.Println("数组的值:", a.Slice())    fmt.Println((a.Get(5)))
//根据索引取值 返回值和是否取到了值 5 true
// 在指定索引前后插入值
_ = a.InsertAfter(9, 10)    _ = a.InsertBefore(0, -1)    fmt.Println(a.Slice())
// 搜索数据项,返回对应的索引    fmt.Println("搜索值,返回对应索引:", a.Search(5))
// 删除    a.Remove(0)    fmt.Println(a.Slice())
// 并发安全 写锁操作    a.LockFunc(func(array []int) {
//将最后一项的值改为100       array[len(array)-1] = 100
})    fmt.Println(a)
//"[0,1,2,3,4,5,6,7,8,9,100]"
// 并发安全 读锁操作    a.RLockFunc(func(array []int) {       fmt.Println(array[len(array)-1])
//100
})
// 清空数组    a.Clear()    fmt.Println("清空数组之后:", a.Slice())
}

打印结果

image.png

数组出栈

  1. 数组出栈使用Pop*关键字
  2. 数组可以按顺序出栈,而gf提供的另外一个数据类型gmap的pop*方法是随机出栈 (关注我,会在后续的文章中更新说明)
  3. garray中随机出栈,我们可以使用rand()或者popRand()
go
package main import (    "fmt"    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g" )
//数组出栈 pop 数组可以按顺序出栈 map的pop是随机出栈
func main() {    a := garray.NewFrom([]interface{
}{1, 2, 3, 4, 5, 6
})    fmt.Println(a.PopLeft())    fmt.Println(a.PopLefts(2))    fmt.Println(a.PopRight())    fmt.Println(a.PopRights(2))    fmt.Println(a)
//全部出栈后 数组为空    /**    打印结果:    1 true    [2 3]    6 true    [4 5]    []    */
// 有什么办法能像map一样随机出栈呢? 在garray中我们使用rand()或者popRand()
a1 := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7
})    fmt.Println("----------")    fmt.Println(a1.Rand())
//随机取值    fmt.Println(a1.PopRands(2))
//随机出栈    fmt.Println(a1)
}

包含判断

  1. 注意:Contains()是区分大小写
  2. 空值过滤使用:FilterEmpty
  3. nil过滤使用:FilterNil
go
package main import (    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g" )
//包含 contains 区分大小写
func main() {
var a garray.Array    a.Append("a")    g.Dump(a.Contains("a"))
//true    g.Dump(a.Contains("A"))
//false
// 空值过滤
a1 := garray.NewFrom([]interface{
}{0, 1, "2", nil, false, g.Slice{
}, "王中阳"
})    a2 := garray.NewFrom([]interface{
}{0, 1, "2", nil, false, g.Slice{
}, "王中阳"
})    g.Dump("empty过滤:", a1.FilterEmpty())
//empty过滤:"[1,2,"王中阳"]"    g.Dump("nil过滤:", a2.FilterNil())
//nil过滤:"[0,1,2,"false","[]","王中阳"]"
a3 := garray.NewFrom([]interface{
}{1, 2, 3, 4, 5, 6, 7
})    g.Dump("数组翻转:", a3.Reverse())    g.Dump("数组随机排序:", a3.Shuffle())
}

打印结果

image.png

排序数组

  1. 我们可以自定义NewSortedArray的排序规则,以实现是升序数组还是降序数组;
  2. 排序数组还有唯一性校验的功能:garray.SetUnique(true)
  3. gf框架的gutil工具包定义好了常用的排序规则
go
package main import (    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g"    "github.com/gogf/gf/util/gutil" )
//我们可以自定义NewSortedArray的排序规则,以实现是升序数组还是降序数组;排序数组还有唯一性校验的功能
func main() {
//自定义排序数组:降序排列
a := garray.NewSortedArray(func(a, b interface{
}) int {
if a.(int) < b.(int) {
return 1
}
if a.(int) > b.(int) {
return -1
}
return 0
})
// 排序规格可以使用gutil中定义好的    a.Add(2)
//数组的赋值用add map的赋值用set    a.Add(1)    a.Add(3)    g.Dump("a:", a)
//打印结果:"[3,2,1]"
//升序数组
a2 := garray.NewSortedArray(gutil.ComparatorInt)    a2.Add(2)    a2.Add(1)    a2.Add(3)    g.Dump("a2:", a2)
// 添加重复元素    a2.Add(2)    g.Dump("a2添加重复元素后:", a2)    a2.SetUnique(true)
//设置不允许重复元素    g.Dump("a2设置不允许重复元素之后:", a2)
}

打印结果

image.png

join、chunk、merge

  1. 数据项串联是相当常用的场景,比如多个id以逗号分隔入库存储,我们使用join关键字即可
  2. garray支持将一个数组拆分成指定数量的二维数组,使用chunk关键字
  3. garray支持使用merge关键字合并数组
go
package main import (    "fmt"    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g" )
func main() {
//join 串联 常用于逗号分割
a := garray.NewFrom(g.Slice{1, 2, 3, 4, 5
})    fmt.Println("串联结果:", a.Join("_"))
//1_2_3_4_5
//数组拆分 chunk    fmt.Println("数组拆分:", a.Chunk(2))
//[[1 2] [3 4] [5]]
// 数组合并 可以合并数组 也可以合并slice(原生切片和g.Slice都支持)
a1 := garray.NewFrom(g.Slice{1, 2
})    a2 := garray.NewFrom(g.Slice{3, 4
})    s1 := g.Slice{5, 6
}    s2 := []string{"7", "8"
}    s3 := []int{9, 0
}    a1.Merge(a2)    a1.Merge(s1)    a1.Merge(s2)    a1.Merge(s3)    fmt.Println("合并结果:", a1)
// [1,2,3,4,5,6,7,8,9,0]
}

打印结果:

image.png

遍历

  1. garray天然支持升序遍历和降序遍历

  2. 函数Iterator()是IteratorAsc()的别名

go
package main import (    "fmt"    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g" )
//数组遍历 iterate
func main() {    a := garray.NewFrom(g.Slice{"a", "b", "c"
})    fmt.Println("升序遍历结果")    a.Iterator(func(k int, v interface{
}) bool {       fmt.Printf("%v,%v \n", k, v)
return true
})
// 数组倒序遍历    fmt.Println("倒序遍历结果:")    a.IteratorDesc(func(k
int, v interface{
}) bool {       fmt.Printf("%v,%v \n", k, v)
return true
})
}

打印结果

image.png

遍历修改 walk函数

非常好用!!!

看到这个方法,更坚信了我一个观点:GF的作者一定写了几年PHP。

go
package main import (    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g"    "github.com/gogf/gf/util/gconv" )
//walk遍历修改 修改数组的值
func main() {
var a garray.Array    tables := g.Slice{"user", "user_detail"
}    a.Append(tables...)    prefix := "gf_"    a.Walk(func(value interface{
}) interface{
} {
return prefix + gconv.String(value)
})    g.Dump(a)
}

打印结果

image.png

序列化和反序列化

  1. 这里重点提一下:gf container容器包下的对象都实现对原生json包的支持,都支持序列化和反序列化。

  2. gf非常重视对序列化的支持,Go学习专栏 文章中介绍了gmap、glist、gqueue、gset、gtree...等gf组件,都是支持序列化和反序列化的。

go

```package main import (    "encoding/json"    "fmt"    "github.com/gogf/gf/container/garray"    "github.com/gogf/gf/frame/g" ) //gf container容器包下的对象都实现对原生json包的支持,即都支持序列化和反序列化 func main() {    //序列化    type student struct {       Name   string       Age    int       Scores *garray.IntArray    }    s := student{       Name:   "王中阳",       Age:    28,       Scores: garray.NewIntArrayFrom([]int{100, 98}),    }    bytes, _ := json.Marshal(s)    g.Dump(bytes) //{"Name":"王中阳","Age":28,"Scores":[100,98]}    //反序列化    data := []byte(`{"Name":"王中阳","Age":28,"Scores":[100,98]}`)    s2 := student{}    _ = json.Unmarshal(data, &s2)    fmt.Println(s2) //{王中阳 28 [100,98]} }``

## 打印结果

![image.png](/images/juejin/041_______GoFrame_garray_PHP_array____/1331e0068b75b21d.webp)

# 总结

综上我们了解到:

1.  garray支持设置并发安全开关
2.  支持排序数组
3.  支持数组出栈、包含判断、join、chunk、merge等常用的工具方法
4.  天然支持升序遍历、遍历修改
5.  天然支持序列化和反序列化

大家是不是明显感觉到GoFrame的garray比PHP的array还要好用。

更加坚信GoFrame的作者是写过PHP的😄

# 一起学习,升级打怪

公众号:程序员升级打怪之旅

微信号:wangzhongyang1993

GitHub:[github.com/wangzhongya…](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fwangzhongyang007 "https://github.com/wangzhongyang007")

AI 应用开发训练营 · 现在开始

别只收藏 AI 文章,今天就做出第一个能上线的项目

文章解决认知,项目才证明能力。把 Agent、RAG、MCP 变成可运行、可展示、可面试表达的成果,现在就从一次岗位准备度自测开始。

立即开始 AI 岗位准备度自测 查看训练营路线
真实学员成果先看成果,再决定是否开始 →
  1. 01选方向
  2. 02做项目
  3. 03出成果
  4. 04拿面试