数组

切片其实和数组区别不大,你可以把它当成是动态数组来看待。它可以按需自动改变大小,使用这种结构,可以更方便的管理和使用数据集合。

声明、初始化

  • 切片和数组在声明上的区别在于没有指定固定长度
slice := []int{1, 22, 333}
slice := []int{1, 2, 3, 4}
slice := []int{1: 1, 3: 4}
  • 空切片与 nil 切片

它们的长度和容量都是 0,但是它们指向底层数组的指针不一样。 nil 切片意味着指向底层数组的指针为 nil,表示不存在的切片; 空切片对应的指针是个地址,空切片表示一个空集合。

var slice []int    // nil 切片 ( slice == nil )

slice := []int{} // 空切片

// 分配内存
slice = make([]int, 10)

// 直接声明并分配内存
slice := make([]int, 10)
slice[0] = 123
slice[1] = 110
slice[2] = 234
  • 声明具有容量的切片
    slice := make([]int, 5, 10)
    

使用内置的 make 函数时,需要传入一个参数,指定切片的长度,如果不指定容量,那么切片的容量和长度将会一样。

可以指定切片的容量,这个容量对应的是切片底层数组的。

因为切片的底层是数组,所以创建切片时,如果不指定字面值的话,默认值就是数组的元素的零值。 如果指定了容量是 10,但是这里只能访问 5 个元素,因为切片的长度是 5,剩下的 5 个元素,需要切片扩充后才可以访问。

容量必须大于等于长度,不能创建长度大于容量的切片的。

  • 基于现有的数组或者切片创建
slice := []int{1, 2, 3, 4, 5}
slice1 := slice[:]
slice2 := slice[0:]
slice3 := slice[:5]
  • 切片会共用底层数组
slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:3]

newSlice[0] = 10

fmt.Println(slice)
fmt.Println(newSlice)

切片扩容

切片算是一个动态数组,可以使用 append 函数扩容,append 函数可以为一个切片追加一个元素。 至于如何增加、返回的是原切片还是一个新切片、长度和容量如何改变这些细节,append 函数都会自动处理。 内置的 append 是一个可变参数的函数,可以同时追加多个值。

slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:3]

newSlice=append(newSlice,10)
fmt.Println(newSlice)
fmt.Println(slice)

如果切片的底层数组没有足够的容量,会新建一个底层数组,把原来数组的值复制到新底层数组里,再追加新值,这时不会影响原来的底层数组。 所以一般在创建新切片的时候保持长度和容量一样,这样在 append 的时候就不会因为共用底层数组而引起其他的问题。

当然,特殊需求特殊对待,如果你知道自己在做什么,那就去做吧,不需要理会这些。

扩容策略:容量小于 1024 个时直接翻倍;容量超过 1024 时,扩容为 1.25 倍。

切片 copy

这里的 copy 不是引用传递,切片起始指针会改变

s1 := []int{1, 2,3, 4, 5}
s2 := make([]int, 5)
copy(s2, s1)
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", s2)

基本概念

数组和切片在使用时其实息息相关、也很密切,概念上,只会和数组做一下区分

  1. 声明slice时候,方括号内为空
  2. 传递切片时传递的不是副本、而是指针
  3. 其本身并不是数组,它指向底层数组
  4. 作为变长数组的替代方案,可以关联底层数组的局部或者全部
  5. 可以直接创建或从底层数组生成
  6. 一般使用make创建
  7. 如果多个切片指向相同的底层数组,其中一个值改变会影响全部
  8. meke([]T, len, cap) cap 省略时和 len 相同
  9. len() 获取元素个数,cap() 获取容量

底层设计

type slice struct {
    array unsafe.Pointer    // 指向数组的指针
    len   int                // 长度
    cap   int                // 容量
}

点击查看 切片底层设计

array 为底层数组,len 为实际存放的个数,cap 为总容量。使用 make 对 slice 进行初始化,也可以类似于数组的方式进行初始化。 当使用 make 函数来对 slice 进行初始化时,第一个参数为切片类型,第二个参数为 len,第三个参数可选,如果不传入,则cap等于len。 通常传入 cap 参数来预先分配大小的 slice,避免频繁重新分配内存。

切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对底层数组的抽象。

因为基于数组实现,所以它的底层的内存是连续分配的,效率非常高,还可以通过索引获得数据,可以迭代以及垃圾回收优化的好处。

切片与字符串

前面有提到 string 的底层设计是一个 byte 的数组,有也可以进行切片操作。

str := "hello world"
s1 := str[0:5]
fmt.Println(s1)
powered by Gitbook该文件修订时间: 2020-04-10 10:05:54

results matching ""

    No results matching ""

    results matching ""

      No results matching ""