Slice Slice
Primer
Think about a problem in the Go language already exists array types, and why do you need a slice type it? Therefore, we need to understand the limitations of array type properties brought about by:
When defining the array must be a good its predetermined length and type can not be changed, the type of data types including length +
When the array as a parameter, the argument must be an array of the same length which is the same type
When a member of the array is full, you can not add new members
Therefore, most of the reason is sliced out the birth address the limitations of arrays
Three Questions soul
What is a slice?
Slice
Slice
is a sequence of variable length have the same data element typeSlices are made based on one array type package
Slice support automatic expansion
Slice is a reference type, including its internal structure
地址
长度
and容量
Slice length and capacity
Slice has its own length and capacity, we can use the built-
len()
seeking function of the length, use the built-incap()
function evaluation slice capacitySlice of nature
Essence of the slice is encapsulated array
It contains three information: pointers underlying array, the length of the slice, the slice of capacity
Case
A conventional arraya := [8]int[0,1,2,3,4,5,6,7,8]
, the slices1 := a[:5]
shown below
slice s2 := a[3:6]
Slice is doing?
Slice generally used for fast integration of a data set
How to use a slice?
Defined sections
var name []T // name 代表变量名 // T 表示切片中的元素类型
func main() { // 声明切片类型 var a []string //声明一个字符串切片 var b = []int{} //声明一个整型切片并初始化 var c = []bool{false, true} //声明一个布尔切片并初始化 var d = []bool{false, true} //声明一个布尔切片并初始化 fmt.Println(a) //[] fmt.Println(b) //[] fmt.Println(c) //[false true] fmt.Println(a == nil) //true fmt.Println(b == nil) //false fmt.Println(c == nil) //false // fmt.Println(c == d) //切片是引用类型,不支持直接比较,只能和nil比较 }
Based on the definition of an array slice
Since the primary is an array of slices, so we can re-define array-based
func main() { // 基于数组定义切片 a := [5]int{55, 56, 57, 58, 59} b := a[1:4] //基于数组a创建切片,包括元素a[1],a[2],a[3] fmt.Println(b) //[56 57 58] fmt.Printf("type of b:%T\n", b) //type of b:[]int }
c := a[1:] //[56 57 58 59] d := a[:4] //[55 56 57] e := a[:] //[55 56 57 58 59]
Then slice sliced
In addition to the array obtained by slicing, the slice can be obtained by slicing
func main() { //切片再切片 a := [...]string{"北京", "上海", "广州", "深圳", "成都", "重庆"} fmt.Printf("a:%v type:%T len:%d cap:%d\n", a, a, len(a), cap(a)) b := a[1:3] fmt.Printf("b:%v type:%T len:%d cap:%d\n", b, b, len(b), cap(b)) c := b[1:5] fmt.Printf("c:%v type:%T len:%d cap:%d\n", c, c, len(c), cap(c)) }
Export
a:[北京 上海 广州 深圳 成都 重庆] type:[6]string len:6 cap:6 b:[上海 广州] type:[]string len:2 cap:5 c:[广州 深圳 成都 重庆] type:[]string len:4 cap:4
When using sections were then sliced, the index can not exceed the original length of the array, or an error occurs in the index out of bounds
Use
make()
Constructor slicedmake([]T, size, cap) // T 切片的元素类型 // size 切片中元素的数量 //cap 切片的容量
func main() { a := make([]int, 2, 10) fmt.Println(a) //[0 0] fmt.Println(len(a)) //2 fmt.Println(cap(a)) //10 }
Can not be directly compared between slices
Between slices is not directly comparable, we can not use the
==
operator to determine whether the element contains two identical sectionsSlice the only legitimate comparison between the operation and are
nil
relativelyA
nil
slice value is not the underlying array 一个
nil
值的切片的长度和容量都是0
一个长度和容量都是
0
的切片不一定就是nil
var s1 []int //len(s1)=0;cap(s1)=0;s1==nil s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
>> 要判断一个切片是否为空,要用
len(s) == 0
来判断,不能用s == nil
来判断切片的赋值拷贝
拷贝前后的变量共享底层数组,对一个切片的修改会影响到另一个切片的内容
func main() { s1 := make([]int, 3) //[0 0 0] s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组 s2[0] = 100 fmt.Println(s1) //[100 0 0] fmt.Println(s2) //[100 0 0] }
切片遍历
func main() { s := []int{1, 3, 5} for i := 0; i < len(s); i++ { fmt.Println(i, s[i]) } for index, value := range s { fmt.Println(index, value) } }
添加元素
Go 语言的内建函数
append()
可以为切片动态添加元素每个切片都会指向一个底层数组,这个数组能容纳一定数量的元素,当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时切片指向的底层数组就会更换
“扩容”操作旺旺发生在
append()
函数调用时func main() { //append()添加元素和切片扩容 var numSlice []int for i := 0; i < 10; i++ { numSlice = append(numSlice, i) fmt.Printf("%v len:%d cap:%d ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice) } }
输出
[0] len:1 cap:1 ptr:0xc0000a8000 [0 1] len:2 cap:2 ptr:0xc0000a8040 [0 1 2] len:3 cap:4 ptr:0xc0000b2020 [0 1 2 3] len:4 cap:4 ptr:0xc0000b2020 [0 1 2 3 4] len:5 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5] len:6 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc0000b8000 [0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc0000b8000
结论:
append()
函数将元素追加到切片的最后并返回该切片- 切片
numSlice
的容量按照 1, 2, 4, 8, 16这样的规则自动就行扩容,每次扩容后都是扩容前的两倍
append
支持一次添加多个元素var citySlice []string // 追加一个元素 citySlice = append(citySlice, "北京") // 追加多个元素 citySlice = append(citySlice, "上海", "广州", "深圳") // 追加切片 a := []string{"成都", "重庆"} citySlice = append(citySlice, a...) fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]
复制切片(独立的切片)
抛出一个小问题:
由于切片是引用类型,a与b实际上引用的是同一个内存地址,所以修改b切片的同时a切片的值也会同时改变
func main() { a := []int{1, 2, 3, 4, 5} b := a fmt.Println(a) //[1 2 3 4 5] fmt.Println(b) //[1 2 3 4 5] b[0] = 1000 fmt.Println(a) //[1000 2 3 4 5] fmt.Println(b) //[1000 2 3 4 5] }
如何实现修改
切片b
但切片a
保持不变?copy()
函数的使用copy(destSlice, srcSlice []T) // destSlice 目标切片 // srcSlice 数据来源切片
func main() { // copy()复制切片 a := []int{1, 2, 3, 4, 5} c := make([]int, 5, 5) copy(c, a) //使用copy()函数将切片a中的元素复制到切片c fmt.Println(a) //[1 2 3 4 5] fmt.Println(c) //[1 2 3 4 5] c[0] = 1000 fmt.Println(a) //[1 2 3 4 5] fmt.Println(c) //[1000 2 3 4 5] }
删除元素
Go 语言中没有删除切片元素的专门方法
func main() { // 从切片中删除元素 a := []int{30, 31, 32, 33, 34, 35, 36, 37} // 要删除索引为2的元素 a = append(a[:2], a[3:]...) fmt.Println(a) //[30 31 33 34 35 36 37] }
通过修改切片的整体值来实现元素的删除,如从切片a中删除索引为
index
的元素,操作方法是a = append(a[:index], a[index+1:]...)