当前位置: 首页 > news >正文

简单易懂,解析Go语言中的Slice切片

目录

  • 2. slice 切片
    • 2.1 初始化
    • 2.2 源代码
    • 2.3 拷贝
    • 2.4 扩容
    • 2.5 切片表达式

2. slice 切片

2.1 初始化

var初始化一个nil切片,不分配内存,(并不是空切片);make 可以指定长度和容量;字面量可以根据长度自动设定长度

若未指定容量,则容量默认等于长度

func main() { // len cap 值已标注在后面
   var v1 []int				    // 0 0
   v2 := make([]int, 0)		    // 0 0
   v3 := make([]int, 5)		    // 5 5
   v4 := make([]int, 5, 10)	    // 5 10
   v5 := []int{}				// 0 0
   v6 := []int{1, 2, 3, 4, 5}	// 5 5
   v7 := *new([]int)            // 0 0 nil切片,不是空切片
}

还可以从数组,切片截取来进行初始化。

  • 切片长度根据截取长度定。
  • 若从切片截取,容量保持跟原切片一致;从数组截取,容量为数组长度 - 起始截取位置;
  • 切片截取时,只考虑容量,不考虑长度;即不超过原数组/切片容量就可以截取
func main() {
   a := [10]int{1, 2, 3, 4, 5}
   s1 := a[1:6]
   s2 := s1[0:9]
   s3 := s1[0:10] //panic: runtime error: slice bounds out of range [:10] with capacity 9
   fmt.Println(len(a), cap(a))
   fmt.Println(len(s1), cap(s1))
   fmt.Println(len(s2), cap(s2))
   /*
   10 10
   5 9
   9 9
    */
}

2.2 源代码

切片依托于底层数组实现

type slice struct {
	array unsafe.Pointer // 指向底层数组
	len   int            // 切片长度
	cap   int            // 容量
}

由于指向的是底层数组,所以在使用数组/切片创建切片的时候,切片会与原数组/切片共用一部分内存,在对切片进行修改的时候,有可能会将原数据一起修改

但扩容之后,数据会被复制到新切片中,此时底层数组就不一样了,不会发生上述情况

//未发生扩容
func main() {
	a := [10]int{1, 2, 3, 4, 5, 6, 7}
	s1 := a[1:5]
	s2 := s1[0:6]
	s1 = append(s1, 10)
	s2[2] = 100
	fmt.Println(a) 
	fmt.Println(s1)
	fmt.Println(s2)
	/*
	[1 2 3 100 5 10 7 0 0 0]
	[2 3 100 5 10]
	[2 3 100 5 10 7]
	 */
}
//发生扩容
func main() {
   a := [10]int{1, 2, 3, 4, 5, 6, 7}
   s1 := a[1:5]
   s2 := s1[0:9]
   s1 = append(s1, 10)
   s2 = append(s2, 10)
   s2[2] = 100
   fmt.Println(a)
   fmt.Println(s1)
   fmt.Println(s2)
}
/*  可以看到修改 100 没有改变原数据
[1 2 3 4 5 10 7 0 0 0]
[2 3 4 5 10]
[2 3 100 5 10 7 0 0 0 10]
 */

2.3 拷贝

  • 使用copy(new,old)可以拷贝切片,并且此时两个切片底层数组地址不同。
  • 拷贝时,去两个切片长度的最小值进行拷贝,拷贝过程中不会发生扩容操作。
func main() {
	a := []int{1, 2, 3, 4, 5}
	b := make([]int, 3)
	c := make([]int, 10)
	copy(b, a)
	copy(c, a)
	b[0] = 100
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	/*
	[1 2 3 4 5]
	[100 2 3]           //修改不会影响原切片,并且复制不会发生扩容操作
	[1 2 3 4 5 0 0 0 0 0]
	 */
}

2.4 扩容

  • slice扩容时,会先创建一个大数组,再将原数组数据复制进去,最后再执行append操作。
  • 1.18前,大于等于1024,每次扩容25%; 小于1024,每次扩容一倍
  • 1.18后,大于256,扩容后的容量计算公式如下:newcap = oldcap+(oldcap+threshold*3)/4; 小于256,每次扩容一倍
  • 过渡更加平滑,避免了2-1.25的突变
  • 实际扩容后的容量不严格等于计算结果,还要考虑到内存对齐等问题
//扩容后地址改变
func main() {
	a := make([]int, 1, 2)
	fmt.Println(&a[0])
	a = append(a, 1)        //未发生扩容
	fmt.Println(&a[0])
	a = append(a, 2)        //发生扩容
	fmt.Println(&a[0])
	/*
	0xc00000a0d0
	0xc00000a0d0
	0xc0000101c0        //扩容后的地址发生了改变
	 */
}
// 扩容示例
func main() {
	a := make([]int, 256) //第一档
	b := make([]int, 257) //第二档 
	c := make([]int, 512)
	d := make([]int, 1024)
	a = append(a, 1)
	b = append(b, 2)
	c = append(c, 3)
	d = append(d, 4)
	fmt.Println(len(a), cap(a))
	fmt.Println(len(b), cap(b))
	fmt.Println(len(c), cap(c))
	fmt.Println(len(d), cap(d))
}
/*          b扩容后计算结果应为 513.25,向上取整 514 , 但实际结果为 608 这其中就经历了内存对齐
257 512
258 608
513 848
1025 1536
 */

2.5 切片表达式

  • 简单表达式[low:high] 表示截取[low,high);low,high均可省略
  • 扩展表达式[low:high:capmax] 只可省略low;capmax用来限制新切片容量,避免对high后底层数组的元素进行修改
  • 作用于字符串时,生成的结果仍为字符串;扩展表达式不能用于字符串
//扩展表达式
func main() {
	a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	b := a[:5]
	b = append(b, 5555)
	c := a[:5:5]        //限制切片容量为5 添加元素就会触发扩容操作,改变底层数组指向
	c = append(c, 6666)
	fmt.Println(a) // [1 2 3 4 5 5555 7 8 9 10]
}
//用于字符串
func main() {
	a := "lonelysnow"
	b := a[6:]
	//c := a[0:6:6] // invalid operation: 3-index slice of string
	fmt.Println(b)
	fmt.Println(reflect.TypeOf(b)) // 查看截取的类型
}
/*
   snow
   string
 */

相关文章:

  • 【JavaEE进阶】数据库连接池
  • BFS算法解决最短路径问题(典型算法思想)—— OJ例题算法解析思路
  • Opengl常用缓冲对象功能介绍及使用示例(C++实现)
  • Qt中QRadioButton的使用
  • 钉钉快捷免登录 通过浏览器打开第三方系统,
  • element ui 组件el-autocomplete的使用方法(输入建议,利用filter和include)
  • 碳基生物的悲歌-DeepSeek思考实现Linux动态库递归收集工具
  • SpringBoot中实现限流和熔断功能
  • 算法系列之贪心算法
  • Java试题:进制转换
  • 分布式锁实现(数据库+Redis+Zookeeper)
  • redis中的Lua脚本,redis的事务机制
  • 深度学习技术文章质量提升指南(基于CSDN评分算法优化)
  • http代理IP怎么实现?如何解决代理IP访问不了问题?
  • 【nextJs】官网demo学习
  • REACT--组件通信
  • 长短期记忆网络:从理论到创新应用的深度剖析
  • 链表-基础训练(二)链表 day14
  • HAProxy介绍与编译安装
  • 宝塔扩容——阿里云如何操作
  • 游戏论|暴君无道,吊民伐罪——《苏丹的游戏》中的政治
  • 安徽亳州涡阳县司法局党组书记刘兴连落马
  • 江西暴雨强对流明显,专家:落雨区高度重叠,地质灾害风险高
  • 上海杨浦:优秀“博主”购房最高可获200万补贴
  • 国家发改委:美芯片药品等领域关税影响全球科技发展,损害人类共同利益
  • 外卖员投资失败负疚离家流浪,经民警劝回后泣不成声给父母下跪