优化golang两个list之间的数据匹配操作

279 阅读2分钟

在用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

希望各位有其它小技巧 或者 又更性能的方法的, 欢迎指出交流哈~