切片踩坑:https://blog.csdn.net/a775189488/article/details/114278113

切片技巧:https://www.liwenzhou.com/posts/Go/slice_tricks/

截取

s = s[low : high : max]
low为截取的起始下标(含),
high为窃取的结束下标(不含high),
max为切片保留的原切片的最大下标(不含max)
即新切片从老切片的low下标元素开始,len = high - low, cap = max - low;
high 和 max一旦超出在老切片中越界,就会发生runtime err,slice out of range。
另外如果省略第三个参数的时候,第三个参数默认和第二个参数相同,即len = cap (这条不准确)

  1. s := []int{1, 1, 1, 1, 1}
  2. s1 := s[1:3]
  3. fmt.Println(s1)
  4. fmt.Println(len(s1)) // 2
  5. fmt.Println(cap(s1)) // 4
  6. s := []int{1, 1, 1, 1, 1}
  7. s1 := s[1:3:4]
  8. fmt.Println(s1)
  9. fmt.Println(len(s1)) // 2
  10. fmt.Println(cap(s1)) // 3
  1. 不会改变原数组 s
  2. func myAppend(s []int) []int {
  3. s = append(s, 100)
  4. return s
  5. }
  6. 会改变原数组 s
  7. func myAppendPtr(s *[]int) {
  8. // 会改变外层 s 本身
  9. *s = append(*s, 100)
  10. return
  11. }

拷贝

  1. b := make([]int, 4)
  2. copy(b, a)
  3. b := make([]int, 0 , 4)
  4. copy(b, a) //这种没效果
  5. // 拷贝方式比较慢,不过添加元素比较快
  6. b = append([]T(nil), a...)
  7. b = append(a[:0:0], a...)

剪切或删除操作可能引起的内存泄露

需要特别注意的是。如果切片a中的元素是一个指针类型或包含指针字段的结构体类型(需要被垃圾回收),
普通的剪切和删除的代码会存在一个潜在的内存泄漏问题:一些具有值的元素仍被切片a引用,因此无法被垃圾回收机制回收掉。
下面的代码可以解决这个问题

  1. // 剪切
  2. copy(a[i:], a[j:])
  3. for k, n := len(a)-j+i, len(a); k < n; k++ {
  4. a[k] = nil // 或类型T的零值
  5. }
  6. a = a[:len(a)-j+i]
  7. // 删除
  8. copy(a[i:], a[i+1:])
  9. a[len(a)-1] = nil // 或类型T的零值
  10. a = a[:len(a)-1]

过滤

  1. // 这代码是真的骚,牛啊
  2. n := 0
  3. for _, x := range a {
  4. if keep(x) {
  5. a[n] = x // 保留该元素
  6. n++
  7. }
  8. }
  9. a = a[:n] // 截取切片中需保留的元素

扩容策略

  1. func growslice(et *_type, old slice, cap int) slice {
  2. newcap := old.cap
  3. doublecap := newcap + newcap
  4. if cap > doublecap {
  5. newcap = cap
  6. } else {
  7. if old.len < 1024 {
  8. newcap = doublecap
  9. } else {
  10. for 0 < newcap && newcap < cap {
  11. newcap += newcap / 4
  12. }
  13. if newcap <= 0 {
  14. newcap = cap
  15. }
  16. }
  17. }
  18. }

image.png