在用golang处理业务的数据拼装时, 我们可能会经常遇到 两个列表之间的数据匹配;很多初级的程序员喜欢用嵌套for去匹配, 这样匹配效率低下,数据多的时候, 可能1-2小时都跑不完.... 下面有一个优化的方法,可以参考下
业务背景:
由于用户数据表user 字段很多, 将字段拆开成两份, 一份作为索引表(基础数据 user_index 存放用户id, 昵称,注册方式等几个常用的字段),一份作为取模后的详情表 (用户注册详细资料)
其中一个列表是用户的 userid , 另一个列表是用户详情,也带userid;
现在需求是把对应详情的数据给拼接起来:
type UserBaseInfo struct {
UserId int64
UserName string
Gender int32
RegType int32
Avatar string
Level int32
}
// 用户帖子信息
type UserArticleInfo struct {
UserId int64
ArticleId int32
Bid int32
Title string
Body string
....
userInf *UserBaseInfo
}
func GetAuthorInfo(userIds []int64)(resp []*UserArticleInfo){
var userInf []*UserBaseInfo
// ... 查库获取 用户基础信息; 并放入 userInf 变量中
var UserArticleInfo []*UserArticleInfo
// ... 查库 获取各个作者帖子信息 放入 UserArticleInfo 切片
// 此时两个数组 已经有内容, 但是需要把用户基础信息拼接进去; 我们还是需要比对两个数组中的 userId去拼接; 如果两个列表的数量上万;那处理数据的复杂度就是 O(n的2次方);
for k,v := range UserArticleInfo {
for x,y := range userInf {
if v.UserId == y.UserId {
v.userInf = y
}
}
}
// 像上面的示例 如果数据很多,处理起来独会非常慢
}
我们需要优化时间复杂度, 到 O(n) 我们先把其中一个数组(用户基本信息列表UserBaseInfo)转化成map , 以userId为key, UserBaseInfo为value
// 我们先
userBaseData := map[int64]*UserBaseInfo{}
for _,y := range userInf {
userBaseData[y.UserId] = y
}
// 利用map的高效查找 进行匹配
for _,v := range UserArticleInfo {
if ok,uf := userBaseData[v.UserId];ok {
v.userInf = uf
}
}
这样就可以实现高效匹配了;
这里还有一个切片的小技巧, 如果你需要在循环中修改切片, 最好使用指针切片的方式。
指针结构体的切片的好处就是传输效率高,节省内存,可以直接修改数组成员值;
因为我们项目经常用到这种方法, 就需要把这个切片转换成map的操作抽离出来,变成公共函数
需要golang支持泛型的go版本 (golang 1.18+):
// SliceToMap 指定类型slice转map
func SliceToMap[T comparable, V any](list []V, keyName string) map[T]V {
data := map[T]V{}
for _, v := range list {
valof := reflect.ValueOf(v)
//fmt.Printf("typeof: %+v\n", valof.Kind())
if valof.Kind().String() == "ptr" {
valof = valof.Elem()
}
item := valof.FieldByName(keyName)
if k, ok := item.Interface().(T); ok {
data[k] = v
}
}
return data
}
使用方法:
userBaseMap := SliceToMap[int64](userInf, "UserId") // 返回的就是 以userId为key的map
希望各位有其它小技巧 或者 又更性能的方法的, 欢迎指出交流哈~