[TOC] # slice **注意,切片代表的窗口是无法向左扩展的** **顺便提一下把切片的窗口向右扩展到最大的方法。对于s4来说,切片表达式s4\[0:cap(s4)\]就可以做到** 一个切片的容量可以被看作是透过这个窗口最多可以看到的底层数组中元素的个数 切片并不是数组或数组指针,它通过内部指针和相关属性引用数组片段,以实现变长方案 slice并不是真正意义上的动态数组,而是一个引用类型.slice总是指向一个底层array, slice的声明也可以像array一样,只是不需要长度 可以把切片看做是对数组的一层简单的封装,因为在每个切片的底层数据结构中,一定会包含一个数组。数组可以被叫做切片的底层数组,而切片也可以被看作是对数组的某个连续片段的引用。 在这种情况下,切片的容量实际上代表了它的底层数组的长度 ~~~ [low:high:max] low起点 high终点(不包括此下标) cap = max-low, 容量 ~~~ ![](https://box.kancloud.cn/bf3568d1649ed16f6f6808b7140cc0a5_795x327.png) ~~~ func main() { a := []int{1, 2, 3, 4, 5} s := a[0:3:5] fmt.Println("s = ", s) //长度 fmt.Println("len(s) = ", len(s)) //容量 fmt.Println("cap(s) = ", cap(s)) } ~~~ 输出 ~~~ s = [1 2 3] len(s) = 3 cap(s) = 5 ~~~ 多维的切片 ~~~ [][]int{{10,20},{30}} ~~~ ## 数组和切片的区别 ~~~ func main() { a := [5]int{} fmt.Printf("len = %d, cap = %d\n", len(a), cap(a)) s := []int{} fmt.Println(len(s), cap(s)) s = append(s, 11) fmt.Printf("append: len = %d, cap = %d\n", len(s), cap(s)) } ~~~ 输出 ~~~ len = 5, cap = 5 0 0 append: len = 1, cap = 1 ~~~ ## 切片的创建 ~~~ func main() { //自动推导类型,同时初始化 s1 := []int{1, 2, 3, 4} fmt.Println("s1 = ", s1) //借助make函数,格式make(切片类型,长度,容量) s2 := make([]int, 5, 10) fmt.Println(len(s2), cap(s2)) //没有指定容量,容量和长度一样 s3 := make([]int, 5) fmt.Println(len(s3), cap(s3)) } ~~~ ## 切片的截取 ![](https://box.kancloud.cn/3411f00c7fd85dd768427bdbc8a8479a_913x393.png) ## 切片和底层数组的关系 会改变底层数组 ~~~ func main() { a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} //新切片 s1 := a[2:5] s1[1] = 666 fmt.Println("s1 = ", s1) fmt.Println("a = ", a) //另外切片 s2 := s1[2:7] s2[2] = 777 fmt.Println("s2 = ", s2) fmt.Println("a = ", a) } ~~~ 输出 ~~~ s1 = [2 666 4] a = [0 1 2 666 4 5 6 7 8 9] s2 = [4 5 777 7 8] a = [0 1 2 666 4 5 777 7 8 9] ~~~ ## 易错 一个切片是以另一个切片为基础切,容量一样那么都是指向同一个东西 ~~~ func main() { data := []string{"red", "1", "black", "1", "pink", "red"} out := data[:1] for _, word := range data { fmt.Println("-----------") i:=0 //比较取出的word是否在out中存在 for ; i<len(out); i++ { if word == out[i] { fmt.Println(len(out)) break } } if i== len(out) { out = append(out, word) fmt.Println(word) } } fmt.Println(data) fmt.Println(out) } ~~~ ## append函数 会智能的底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据 ~~~ func main() { data := []string{"red", "1", "black", "1", "pink"} fmt.Printf("%v\n", data) out := data[:0] fmt.Printf("%v\n", out) for _, str := range data{ if str != "1" { out = append(out, str) } } fmt.Printf("%v\n", data) //感觉奇怪 fmt.Printf("%v\n", out) //[red 1 black 1 pink] //[] //[red black pink 1 pink] //[red black pink] } ~~~ 如果不想里面有空的元素,就make的len为0,cap为0 ~~~ slice := make([]string, 0) slice = append(slice, "111") slice = append(slice, "222") slice = append(slice, "33") for _, data := range slice { fmt.Println("data----", data) } ~~~ `slice:=append([]int{1,2,3},[]int{4,5,6}...)` `fmt.Println(slice)//[1 2 3 4 5 6]` * 还有种特殊用法,将字符串当作\[\]byte类型作为第二个参数传入 ## copy copy会把一些位置替换 ~~~ func main() { srcSlice := []int{1, 2} dstSlice := []int{6, 6, 6, 6, 6} copy(dstSlice, srcSlice) fmt.Println("dst = ", dstSlice) } ~~~ 输出 ~~~ dst = [1 2 6 6 6] ~~~ ## 作为函数参数 是引用传递,里面改了,外面也一样会改 ~~~ complexArray1 := [3][]string{ []string{"d", "e", "f"}, []string{"g", "h", "i"}, []string{"j", "k", "l"}, } ~~~ 变量complexArray1是\[3\]\[\]string类型的,也就是说,虽然它是一个数组,但是其中的每个元素又都是一个切片。这样一个值被传入函数的话,函数中对该参数值的修改会影响到complexArray1本身吗? 分2种情况,若是修改数组中的切片的某个元素,会影响原数组。若是修改数组的某个元素即a\[1\]=\[\]string{"x"}就不会影响原数组。谨记Go中都是浅拷贝,值类型和引用类型的区别 ## nil 目前已知只有`var s []int`这1种方法才是nil slice。其他情况都不是,因为只有no underlying array才是nil slice 下面几种方法都不是nil slice 1. `s := []int{}`不是nil 2. 删除所有元素也不算是nil,如 ~~~ sli = sli[:cap(sli)] sli = sli[len(sli):] ~~~ 3. 用`make([]int, 0, 0)`也不是nil ~~~ package main import "fmt" func main() { var a []int fmt.Println("a:", a, len(a), cap(a)) b := []int{} fmt.Println("b:", b, len(b), cap(b)) c := make([]int, 0, 0) fmt.Println("c:", c, len(c), cap(c)) d := []int{2, 3, 5, 7, 11, 13} d = d[:cap(d)] d = d[len(d):] fmt.Println("d:", d, len(d), cap(d)) if a == nil { fmt.Println("a is nil") } if b == nil { fmt.Println("b is nil") } if c == nil { fmt.Println("c is nil") } if d == nil { fmt.Println("d is nil") } } ~~~ ## 嵌套 slice可以嵌套包含slice,并且可以嵌套多层。也可以叫做多维slice。另外,array也支持多维array(这里就不提了) 嵌套一层 ~~~ board1 := [][]string{ []string{"_", "_", "_"}, []string{"_", "_", "_"}, []string{"_", "_", "_"}, } ~~~ **支持简写** 比如上面这个例子可以改为 ~~~ b := [][]string{ {"_", "_", "_"}, {"_", "_", "_"}, {"_", "_", "_"}, } ~~~ 嵌套两层 ~~~ board2 := [][][]string{ [][]string{ []string{"_", "_", "_"}, []string{"_", "_", "_"}, []string{"_", "_", "_"}, }, [][]string{ []string{"_", "_", "_"}, []string{"_", "_", "_"}, []string{"_", "_", "_"}, }, [][]string{ []string{"_", "_", "_"}, []string{"_", "_", "_"}, []string{"_", "_", "_"}, }, } ~~~ 赋值 ~~~ board1[0][0] = "X" board2[0][0][0] = "Y" ~~~ 通过嵌套实现了高级数据结构,比如python中列表嵌套字典嵌套列表等等