切片Slice

切片Slice
概念
切片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]

}
切片的底层原理
Go Quiz: 从Go面试题看slice的底层原理和注意事项
Go Quiz: 从Go面试题搞懂slice range遍历的坑

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值