切片slice:切片是对数组的抽象。Go数组的长度在定义后是固定的,不可改变的。
切片的长度和容量是不固定的,可以动态增加元素,切片的容量也会根据情况自动扩容
-
切片的底层数据结构
type slice struct { array unsafe.Pointer len int cap int }
切片slice是个struct结构体,里面实际有个指针array,类型是unsafe.Pointer,也就是个指针,指向存放数据的数组。
len是切片的长度,cap是切片的容量。
-
定义和初始化
-
语法
var slice_var []data_type // 元素类型为data_type的切片 var slice_var []data_type = make([]data_type, len, cap)// cap是切片容量,是make的可选参数 var slice_var []data_type = make([]data_type, len) slice_var := []data_type{} slice_var := make([]data_type, len)
-
示例
package main import "fmt" func printSlice(param []int) { fmt.Printf("slice len:%d, cap:%d, value:%v\n", len(param), cap(param), param) } func main() { slice1 := []int{1} slice2 := make([]int, 3, 100) printSlice(slice1) printSlice(slice2) }
-
-
零值nil
-
如果slice类型的变量定义后没有初始化赋值,那值就是默认值nil。对于nil切片,len和cap函数执行结果都是0。
注意:下例里的slice有赋值,所以slice!=nil。slice2没有赋值,slice2==nil
package main import "fmt" func printSlice(param []int) { fmt.Printf("param len:%d, cap:%d, value:%v\n", len(param), cap(param), param) } func main() { slice := []int{} var slice2 []int fmt.Println("slice==nil", slice==nil) // false printSlice(slice) fmt.Println("slice2==nil", slice2==nil) // true printSlice(slice2) }
-
切片访问:对切片的访问,类似数组一样,可以用下标索引或者range迭代的方式进行。可以参考lesson10和lesson14
package main
import "fmt"
func main() {
slice := make([]int, 3, 10)
/*下标访问切片*/
slice[0] = 1
slice[1] = 2
slice[2] = 3
for i:=0; i<len(slice); i++ {
fmt.Printf("slice[%d]=%d\n", i, slice[i])
}
/*range迭代访问切片*/
for index, value := range slice {
fmt.Printf("slice[%d]=%d\n", index, value)
}
}
切片截取:
类似Python,使用冒号:
来对数组或者切片做截取。
冒号:
截取后的新slice变量底层有个指针,会指向原数组或者原切片的数组空间,对新切片的修改也会影响原数组或者原切片。
package main
import "fmt"
import "reflect"
func printSlice(param []int) {
fmt.Printf("param len:%d, cap:%d, value:%v\n", len(param), cap(param), param)
}
func main() {
slice := []int{}
var slice2 []int
fmt.Println("slice==nil", slice==nil) // false
printSlice(slice)
fmt.Println("slice2==nil", slice2==nil) // true
printSlice(slice2)
// 对数组做切片
array := [3]int{1,2,3} // array是数组
slice3 := array[1:3] // slice3是切片
fmt.Println("slice3 type:", reflect.TypeOf(slice3))
fmt.Println("slice3=", slice3) // slice3= [2 3]
slice4 := slice3[1:2]
fmt.Println("slice4=", slice4) // slice4= [3]
/* slice5->slice4->slice3->array
对slice5的修改,会影响到slice4, slice3和array
*/
slice5 := slice4[:]
fmt.Println("slice5=", slice5) // slice5= [3]
slice5[0] = 10
fmt.Println("array=", array) // array= [1 2 10]
fmt.Println("slice3=", slice3) // slice3= [2 10]
fmt.Println("slice4=", slice4) // slice4= [10]
fmt.Println("slice5=", slice5) // slice5= [10]
}
len()和cap()函数:类似C++的vector里的size和capacity
- len():获取切片的长度,也就是实际存储了多少个元素
- cap(): 获取切片的容量。如果切片的元素个数要超过当前容量,会自动扩容
append():通过append函数给切片加元素
-
append不改变原切片的值,比如下例里的append(slice, 4)并不会改变slice的值
-
只能对切片使用append()函数,不能对数组使用append()
package main import "fmt" func main() { slice := []int{1, 2, 3} // 往原切片里加一个元素 test := append(slice, 4) // append不会改变slice的值,除非把append的结果重新赋值给slice fmt.Println(slice) // [1 2 3] fmt.Println(test) // [1 2 3 4] // 通过append给切片添加切片 temp := []int{1,2} test = append(test, temp...) // 注意,第2个参数有...结尾 fmt.Println(test) // [1 2 3 4 1 2] /*下面对array数组做append就会报错: first argument to append must be slice; have [3]int array := [3]int{1, 2, 3} array2 := append(array, 1) fmt.Println(array2) */ }
copy():拷贝一个切片里的数据到另一个切片
-
语法
copy(dstSlice, srcSlice) // 把srcSlice切片里的元素拷贝到dstSlice切片里
注意事项:只从源切片srcSlice拷贝min(len(srcSlice), len(dstSlice))个元素到目标切片dstSlice里。如果dstSlice的长度是0,那一个都不会从srcSlice拷贝到dstSlice里。如果dstSlice的长度M小于srcSlice的长度N,则只会拷贝srcSlice里的前M个元素到目标切片dstSlice里。
package main
import "fmt"
func main() {
a := []int{1, 2}
b := make([]int, 1, 3) // 切片b的长度是1
copy(b, a) // 只拷贝1个元素到b里
fmt.Println("a=", a) // a= [1 2]
fmt.Println("b=", b) // b= [1]
}
-
slice切片如果是函数参数,函数体内对切片底层数组的修改会影响到实参。比如下例里的change1函数第一行
-
如果在函数体内通过append直接对切片添加新元素,不会改变外部切片的值,比如下例里的change1函数第2行。但是如果函数使用切片指针作为参数,在函数体内可以通过切片指针修改外部切片的值,比如下例里的change2函数
package main import "fmt" func change1(param []int) { param[0] = 100 // 这个会改变外部切片的值 param = append(param, 200) // append不会改变外部切片的值 } func change2(param *[]int) { *param = append(*param, 300) // 传切片指针,通过这种方式append可以改变外部切片的值 } func main() { slice := make([]int, 2, 100) fmt.Println(slice) // [0, 0] change1(slice) fmt.Println(slice) // [100, 0] change2(&slice) fmt.Println(slice) // [100, 0, 300] }