slice详解

646 阅读3分钟

slice初始化的3种方式

  • 通过字面量的方式,例如:
    s1 := []int{1, 2, 3}
    fmt.Printf("s1: %+v, len: %d, cap: %d \n", s1, len(s1), cap(s1))
    // s1: [1 2 3], len: 3, cap: 3

需要注意的是, 通过字面量初始化的slice,它的len和cap都是一样的,等于你定义时候给定的元素个数

  • 通过make关键字对slice进行初始化,例如:
   // s2 := make([]int, 5)
   s2 := make([]int, 5, 10)
    fmt.Printf("s2: %+v, len: %d, cap: %d \n", s2, len(s2), cap(s2))
    // s2: [0 0 0 0 0], len: 5, cap: 10

通过make关键字,我们可以指定len和cap,也可以只指定len,如果只指定len的话,那么这个slice的cap默认就是和len一样

  • 通过对slice或者数组进行切片得到一个新的slice,例如:
    s3 := s2[0:8] // 切片可以在s2的capacity范围之内
    fmt.Printf("s3: %+v, len: %d, cap: %d \n", s3, len(s3), cap(s3))
    // s3: [0 0 0 0 0 0 0 0], len: 8, cap: 10

这个时候,得到slice的len为你切片得到的元素个数,cap则是你切片的对象的cap

slice的运行时所表示的数据结构

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

slice就是一个对数组段的描述符,底层的Data数据存储是基于数组的数据结构,slice由指向数组的指针,Len表示段的长度,Cap表示的是该段的最大容量。

使用切片需要注意的一些东西

  1. 在go中不存在引用传递,都是值传递。通过对数组或者slice切片得到一个新的切片,但是新的切片并不会copy原数组或者slice的数据,它通过新建了一个切片的值,指向了原始的数组,所以,我们修改新切片的数据时,会改变原数组或者切片的值。 例如:
func updateSliceValue() {
    s := []int{0, 1, 2, 3, 4}
    s2 := s[0:1]
    fmt.Printf("原 s: %+v \n", s)
    fmt.Printf("s2: %+v \n", s2)
    s2[0] = 111
    fmt.Printf("s2: %+v \n", s2)
    fmt.Printf("修改s2后, s: %+v \n", s)
}
// 原 s: [0 1 2 3 4]
// s2: [0]
// s2: [111]
// 修改s2后, s: [111 1 2 3 4]
  1. 但是我们要注意的是,我们不应该基于此在方法内直接修改一个slice的元素,然后在程序的后续逻辑继依赖于在方法内修改的值。这里很有可能就会产生大坑。 例如:
func main() {
    s := []int{1, 2, 3}
    modifyValue(s)
    if s[0] == 111 {
        fmt.Println("here")
    } else {
        fmt.Println("not here")
    }
}
func modifyValue(s []int) {
    // logic 1
    s[0] = 111
    // logic 2
}

在上面的这个例子中,如果logic1中的处理,或者logic2中因为有对s的append操作,那么,很有可能会因为append的操作,导致s进行了扩容,扩容后的s指向的内存地址,和传入前的s不再共享同一个底层的数组逻辑。那么在你以后的依赖的逻辑就会出现错误。这也是为什么官方中append使用时候,必须要你使用一个变量进行接收,例如s = append(s, 1)的原因

slice的扩容策略

slice在内存扩容时,针对当前slice的容量大小有不同的扩容策略:

  • 如果申请的容量大于了2倍的当前容量,则会使用申请的容量
  • 如果当前slice的长度小于1024,则以double当前容量的形式进行扩容
  • 如果当前slice的长度大于1024,则会以当前容量 * 25%递进式的扩容,直到扩容的容量大于申请的容量

参考链接

blog.golang.org/slices#TOC_….

blog.golang.org/slices-intr….

dave.cheney.net/2017/04/29/…