Go语言------切片

slice(切片)

在Go语言中,数组是长度不可变的有序存储序列,且在作为参数传递时会复制整个数组,这在很多场景下并不适用。Go语言提供了一种长度可变,且为引用传递的数组的替代结构,切片。
切片是基于数组抽象出来的一个数据结构,原型定义如下:

	struct {
		byte *array;
		unit32 len;
		unit32 cap;
	}

原型结构中包含了三部分:
- 底层数组的指针
- 切片当前长度
- 切片最大容量

声明方式

切片有多种声明方式,既可以通过make来创建,也可以通过从数组或者原始切片中创建

	// 通过make函数创建切片,第二个参数为要初始化为零值的个数,第三个参数切片的初始容量
	// 打印结果为:切片元素:[0 0 0],切片长度:3,切片容量:5 
	// 此时访问 a[3],会报错数组越界
	a := make([]int, 3, 5)
	fmt.Printf("切片元素:%v,切片长度:%d,切片容量:%d \n", a, len(a), cap(a))

	// 通过make函数创建切片,同时指定初始化为零值的个数,此时初始容量和初始长度相等
	// 打印结果为:切片元素:[0 0 0],切片长度:3,切片容量:3
	b := make([]int, 3)
	fmt.Printf("切片元素:%v,切片长度:%d,切片容量:%d \n", a, len(b), cap(b))

	// 直接创建切片并赋值,此时[]中需为空,否则创建出来的就是数组
	// 切片的初始容量和长度为赋值元素的数量
	// 打印结果为:切片元素:[1 2 3],切片长度:3,切片容量:3
	c := []int{1, 2, 3}
	fmt.Printf("切片元素:%v,切片长度:%d,切片容量:%d \n", c, len(c), cap(c))

	// 从原始数组中直接截取切片,切片的截取规则为左闭右开,原始数组可改为原始切片,
	// 如不指定第三个数字,则切片的初始容量为数组的长度或者原始切片的容量
	// 可以通过第三个数字指定切片的初始容量,该长度不能小于截取的长度且不能大于数组的长度或原始切片的容量
	// 打印结果为:d 切片元素:[1 2 3],d 切片长度:3,d 切片容量:7 
	//			 e 切片元素:[1 2 3],e 切片长度:3,e 切片容量:5 
	arr := [7]int{1, 2, 3, 4, 5, 6, 7}    
// 	arr := []int{1, 2, 3, 4, 5, 6, 7}
	d := arr[0:3]
	e := arr[0:3:5]
	fmt.Printf("d 切片元素:%v,d 切片长度:%d,d 切片容量:%d \n", d, len(d), cap(d))
	fmt.Printf("e 切片元素:%v,e 切片长度:%d,e 切片容量:%d \n", e, len(e), cap(e))

	// 空切片
	// 不管是任何一种方式创建的零切片,调用append,cap和len函数时效果都一样
	// 第一种形式的切片其指针为nil,不指向任何的内存地址
	// 打印结果为:f 切片元素:[],f 切片长度:0,f 切片容量:0,f切片的数组地址:0x0
	var f []int
	fmt.Printf("f 切片元素:%v,f 切片长度:%d,f 切片容量:%d,f切片的数组地址:%p \n", f, len(f), cap(f), f)
	
	// 第一种形式的切片其指针不为nil,会指向一个零值地址,此时并未给切片的底层数组分配内存
	// 打印结果为:g 切片元素:[],g 切片长度:0,g 切片容量:0, g切片的数组地址:0x115ac98 
				 h 切片元素:[],h 切片长度:0,h 切片容量:0, h切片的数组地址:0x115ac98 
	g := make([]int, 0)
	h := []int{}
	fmt.Printf("g 切片元素:%v,g 切片长度:%d,g 切片容量:%d, g切片的数组地址:%p \n", g, len(g), cap(g), g)
	fmt.Printf("h 切片元素:%v,h 切片长度:%d,h 切片容量:%d, h切片的数组地址:%p \n", h, len(h), cap(h), h)

追加数据和扩容

向切片中追加数据需要调用append内置函数来实现

需要注意的是,调用append时会返回一个切片的对象,此时一定要将此返回赋值给原切片对象,否则原切片对象不会发生任何改变。

	// append函数追加完成之后,将返回的切片赋值给了b,此时a未发生任何改变
	// 故在实际使用时,一般的使用方法是:a = append(a,4)
	// 打印结果为:切片元素:[1 2 3], 切片底层数组地址:0xc42009c000 
				 a 切片元素:[1 2 3], a 切片底层数组地址:0xc42009c000 
				 b 切片元素:[1 2 3 4], b 切片底层数组地址:0xc42008e030 
	a := []int{1, 2, 3}
	fmt.Printf("切片元素:%v, 切片底层数组地址:%p \n", a, a)
	b := append(a, 4)
	fmt.Printf("a 切片元素:%v, a 切片底层数组地址:%p \n", a, a)
	fmt.Printf("b 切片元素:%v, b 切片底层数组地址:%p \n", b, b)
  • 在未超出切片的当前容量时,底层数组地址不会发生改变
  • 在追加数据的过程中,如果超出了切片的容量,切片会创建一个更大容量的数组,然后将原底层数组拷贝到新数组中,这个过程对使用者是透明的。此时原数组不会发生任何改变。
  • 底层数组扩容的过程中,切片本身的地址不会发生任何改变,改变的是切片对象中的指向底层数组的指针。
  • 切片的容量追加规则:切片容量小于1024时,每次追加的cap都是翻倍,超过1024后,则每次的cap都扩展为原来容量的1.25倍
	// 打印结果为:切片内容:[0 0 0],切片长度:3,切片容量:4,切片地址:0xc42000a080,底层数组地址:0xc42001a080 
				 切片内容:[0 0 0 3],切片长度:4,切片容量:4,切片地址:0xc42000a080,底层数组地址:0xc42001a080 
				 切片内容:[0 0 0 3 4],切片长度:5,切片容量:8,切片地址:0xc42000a080,底层数组地址:0xc42001e0c0 
	a := make([]int, 3, 4)
	fmt.Printf("切片内容:%v,切片长度:%d,切片容量:%d,切片地址:%p,底层数组地址:%p \n", a, len(a), cap(a), &a, a)
	a = append(a, 3)
	fmt.Printf("切片内容:%v,切片长度:%d,切片容量:%d,切片地址:%p,底层数组地址:%p \n", a, len(a), cap(a), &a, a)
	a = append(a, 4)
	fmt.Printf("切片内容:%v,切片长度:%d,切片容量:%d,切片地址:%p,底层数组地址:%p \n", a, len(a), cap(a), &a, a)

底层数组

  • 从数组切出来的切片,在操作切片修改元素时,数组会跟着变化。
	// 数组地址:0xc42001a080 
	a := [3]int{1, 2, 3}
	fmt.Printf("数组地址:%p \n", &a)

	// 从原始数组中切出切片b,第一次追加元素时,会更改原始数组的第三个值为9,且此时追加后切片的底层数组地址未发生改变
	// 打印结果: 
		b 切片内容:[1 2],b 切片长度:2,b 切片容量:3,b 切片地址:0xc42000a080,b 底层数组地址:0xc42001a080 
		b 切片内容:[1 2 9],b 切片长度:3,b 切片容量:3,b 切片地址:0xc42000a080,b 底层数组地址:0xc42001a080 
		数组内容:[1 2 9] 
	b := a[0:2]
	fmt.Printf("b 切片内容:%v,b 切片长度:%d,b 切片容量:%d,b 切片地址:%p,b 底层数组地址:%p \n", b, len(b), cap(b), &b, b)
	b = append(b, 9)
	fmt.Printf("b 切片内容:%v,b 切片长度:%d,b 切片容量:%d,b 切片地址:%p,b 底层数组地址:%p \n", b, len(b), cap(b), &b, b)
	fmt.Printf("数组内容:%v \n", a)


	// 在切片b的基础上再次追加元素,此时元素数量超出了底层数组的容量,会重新创建底层数组,并将原切片复制到新数组中,切片的数组指针指向新的底层数组,数组的容量比之前扩充了2倍。
	// 打印结果:
		b 切片内容:[1 2 9 8],b 切片长度:4,b 切片容量:6,b 切片地址:0xc42000a080,b 底层数组地址:0xc42001c0f0 
		数组内容:[1 2 9] 
	b = append(b, 8)
	fmt.Printf("b 切片内容:%v,b 切片长度:%d,b 切片容量:%d,b 切片地址:%p,b 底层数组地址:%p \n", b, len(b), cap(b), &b, b)
	fmt.Printf("数组内容:%v \n", a)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值