分享
  1. 首页
  2. 文章

Go Slice 高级实践

sxssxs · · 1793 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  1. 以下用法中,类型均使用 int64 做为示例,不处理 interface 。
  2. 代码只是展示实现思路,不一定完善。

合并两个有序切片,新切片仍然有序

func MergeSortedSlice(s1, s2 []int64) []int64 {
 // 从末尾元素开始遍历
 i := len(s1) - 1
 j := len(s2) - 1
 // 合并后的长度
 newLen := len(s1) + len(s2)
 // 合并后的索引,也从末尾元素开始
 newIdx := newLen - 1
 // 创建一个新切片,代表合并后的
 newS := make([]int64, newLen)
 // 将 s1 的内容拷贝到新切片
 for k, v := range s1 {
 newS[k] = v
 }
 // 开始遍历
 for i >= 0 && j >= 0 {
 // 新元素
 var newNum int64
 // 将较大的值赋给新元素,同时向前移动指针
 if newS[i] > s2[j] {
 newNum = newS[i]
 i--
 } else {
 newNum = s2[j]
 j--
 }
 newS[newIdx] = newNum
 newIdx--
 }
 // 如果 s2 还有剩余元素,则剩余元素一定都是最小的,直接放到头部即可
 for j >= 0 {
 newS[newIdx] = s2[j]
 j--
 newIdx--
 }
 return newS
}

根据特定规则过滤元素

func FilterSlice(s []int64, filter func(x int64) bool) []int64 {
 // 返回的新切片
 // s[:0] 这种写法是创建了一个 len 为 0,cap 为 len(s) 即和原始切片最大容量一致的切片
 // 因为是过滤,所以新切片的元素总个数一定不大于比原始切片,这样做减少了切片扩容带来的影响
 // 同时,也有一个问题,因为 newS 和 s 共享底层数组,那么过滤后 s 也会被修改!
 newS := s[:0]
 // 遍历,对每个元素执行 filter,符合条件的加入新切片中
 for _, x := range s {
 if !filter(x) {
 newS = append(newS, x)
 }
 }
 return newS
}

去重

两种思路,循环顺序查找和使用 map 加快查找(引入一个 map 在各方面也是有开销的)。选用哪种,可以通过具体场景的 Benchmark 决定

func RemoveDuplicates(s []int64) []int64 {
 var ret []int64
 for _, v := range s {
 found := false
 for _, v2 := range ret {
 if v == v2 {
 found = true
 break
 }
 }
 if !found {
 ret = append(ret, v)
 }
 }
 return ret
}
func RemoveDuplicates2(s []int64) []int64 {
 ret := s[:0]
 // 利用 struct{}{} 减少内存占用
 assist := map[int64]struct{}{}
 for _, v := range s {
 if _, ok := assist[v]; !ok {
 assist[v] = struct{}{}
 ret = append(ret, v)
 }
 }
 return ret
}

反转

func Reversing(s []int64) []int64 {
 for left, right := 0, len(s)-1; left < right; left, right = left+1, right-1 {
 s[left], s[right] = s[right], s[left]
 }
 return s
}

分块

主要用于当单个切片过大,需要分多次使用的时候,比如网络调用等。

func SliceChunk(s []int64, size int) [][]int64 {
 var ret [][]int64
 for size < len(s) {
 // s[:size:size] 表示 len 为 size,cap 也为 size,第二个冒号后的 size 表示 cap
 s, ret = s[size:], append(ret, s[:size:size])
 }
 ret = append(ret, s)
 return ret
}

类型转换

RPC 中,不同下游接收的类型可能不一样,还有自定义类型,这里提供一个快速转换的方法

s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var newS []int64
// 做法是利用 reflect 直接替换数据指针
// 但是这个不保证在以后的版本中一直可用 ╮(╯▽╰)╭
*(*reflect.SliceHeader)(unsafe.Pointer(&newS)) = *(*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("type:%T value:%v", newS, newS)

未完待续

主要参考:
https://github.com/golang/go/wiki/SliceTricks
官方使用技巧,建议多看看。

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:sxssxs

查看原文:Go Slice 高级实践

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1793 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏