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

【Golang】Slice切片学习+实验代码

一、数组与切片区别

1.声明方式:

数组:需要指定数组的长度,例如 var arr [5]int。

切片:不需要指定长度,例如 var slice []int。

2.内存分配:

数组:声明时分配固定大小的内存空间。

切片:声明时不分配内存,只是定义了一个引用,可以指向一个数组或另一个切片。

3.操作:

数组:不能直接增长或缩小。

切片:可以通过 append 函数动态增长,也可以通过切片操作(如 slice[1:3])来缩小。

切片是一种数据结构,切片不是数组,切片描述的是一块数组 array (指针)len (有效长度)cap

二、切片声明

var slice []int // 直接声明, slice 是一个空切片,没有任何元素,也没有指向任何数组。slice := []int{1,2,3,4,5} // 字面量方式,创建了一个包含五个整数的切片,并赋值给 slice。slice := make([]int, 5, 10) // make创建,创建了一个长度为5,容量为10的切片,并赋值给 slice make 函数是创建切片的常用方法,它接受三个参数:切片类型、长度和容量。slice := array[1:5] // 截取下标的方式,通过切片操作 array[1:5] 来创建一个新的切片,这个切片包含 array 中从索引1到索引4的元素。slice := *new([]int) // new 关键字创建了一个指向 []int 类型切片的指针,并解引用它来得到一个切片。这种方式不常用,因为 new 创建的是指针,而我们通常直接使用 make 或字面量来创建切片。

三、切片内部结构

type Slice struct {Data uintptrLen  intCap  int
}

当切片作为参数传递时,其实就是一个结构体的传递,因为Go语言参数传递只有值传递,传递一个切片就会浅拷贝原切片,但因为底层数据的地址没有变,所以在函数内对切片的修改,也将会影响到函数外的切片,举例:


func modifySlice(s []string) {s[0] = "hello"s[1] = "Golang"fmt.Println("out slice: ", s)
}func main() {s := []string{"hi", "Golang"}modifySlice(s)fmt.Println("inner slice: ", s)
}
// 运行结果
out slice:  [hello Golang]
inner slice:  [hello Golang]

不过这也有一个特例,先看一个例子:

func appendSlice(s []string) {s = append(s, "!!")fmt.Println("out slice: ", s)
}func main() {s := []string{"hi", "Golang"}appendSlice(s)fmt.Println("inner slice: ", s)
}
// 运行结果
out slice:  [hi Golang!!]
inner slice:  [hi  Golang]

因为切片发生了扩容,函数外的切片指向了一个新的底层数组,所以函数内外不会相互影响,因此可以得出一个结论,当参数直接传递切片时,如果指向底层数组的指针被覆盖或者修改(copy、重分配、append触发扩容),此时函数内部对数据的修改将不再影响到外部的切片,代表长度的len和容量cap也均不会被修改。

参数传递切片指针就很容易理解了,如果你想修改切片中元素的值,并且更改切片的容量和底层数组,则应该按指针传递。

四.range遍历切片注意

Go语言提供了range关键字用于for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素,有两种使用方式:

for k,v := range _ { }
for k := range _ { }

第一种是遍历下标和对应值,第二种是只遍历下标,使用range遍历切片时会先拷贝一份,然后在遍历拷贝数据:

	s := []int{1, 2}for k, v := range s {}//会被编译器认为是for_temp := slen_temp := len(for_temp)for index_temp := 0; index_temp < len_temp; index_temp++ {value_temp := for_temp[index_temp]_ = index_tempvalue := value_temp}

不知道这个知识点的情况下很容易踩坑,例如下面这个例子:

package mainimport ("fmt"
)type user struct {name stringage  uint64
}func main() {u := []user{{"张三", 23},{"李四", 19},}for i := range u {if u[i].age == 18 {u[i].age = 20}}// 打印修改后的切片fmt.Println(u)fmt.Println("Hello, World!")
}
//
[{张三 23} {李四 19}]
Hello, World!

因为使用range遍历切片u,变量v是拷贝切片中的数据,修改拷贝数据不会对原切片有影响。

五、扩容策略

切片在扩容时会进行内存对齐,这个和内存分配策略相关。进行内存对齐之后,新 slice 的容量是要 大于等于老 slice 容量的 2倍或者1.25倍,当原 slice 容量小于 1024 的时候,新 slice 容量变成原来的 2 倍;原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。

六、实验

package mainimport "fmt"func main() {// 浅拷贝示例fmt.Println("Shallow Copy Example:")slice1 := []int{1, 2, 3}slice2 := slice1[:] // 使用[:]进行浅拷贝fmt.Println("Original slice:", slice1)fmt.Println("Shallow copied slice:", slice2)// 修改原始切片slice1[0] = 100fmt.Println("Modified original slice:", slice1)fmt.Println("Shallow copied slice after modification:", slice2) // 浅拷贝后的切片也发生了变化// 深拷贝示例fmt.Println("\nDeep Copy Example:")slice3 := make([]int, len(slice1))copy(slice3, slice1) // 使用copy进行深拷贝fmt.Println("Original slice:", slice1)fmt.Println("Deep copied slice:", slice3)// 修改原始切片slice1[0] = 200fmt.Println("Modified original slice:", slice1)fmt.Println("Deep copied slice after modification:", slice3) // 深拷贝后的切片不受影响// 大小切片拷贝代价对比fmt.Println("\nCost of Copying Slices of Different Sizes:")param1 := make([]int, 100)param2 := make([]int, 100000000)// 浅拷贝大切片和小切片smallShallowCopy := param1[:]largeShallowCopy := param2[:]// 深拷贝大切片和小切片smallDeepCopy := make([]int, len(param1))copy(smallDeepCopy, param1)largeDeepCopy := make([]int, len(param2))copy(largeDeepCopy, param2)// 展示深拷贝和浅拷贝的结果fmt.Println("Small shallow copy:", smallShallowCopy)fmt.Println("Large shallow copy:", largeShallowCopy)fmt.Println("Small deep copy:", smallDeepCopy)fmt.Println("Large deep copy:", largeDeepCopy)
}

结果


http://www.mrgr.cn/news/66793.html

相关文章:

  • SpringMVC总结 我的学习笔记
  • docker加载目录中所有的镜像
  • 电脑开机显示无信号然后黑屏怎么办?
  • 【每日 C/C++ 问题】
  • asp.net网站项目如何设置定时器,定时获取数据
  • 计算机网络:网络层 —— 路由信息协议 RIP
  • 全面解析:网络协议及其应用
  • SQLAlchemy 介绍与实践
  • 《XGBoost算法的原理推导》12-2 t轮迭代中对样本i的预测值 公式解析
  • DICOM标准:DICOM图像核心属性概念详解——关于参考帧、病人位置、病人方位、图像位置和图像方向、切片位置、图像像素等重要概念解析
  • 双十一热销中 Witsbb健敏思小蓝条钙镁锌霸榜品类TOP1,行业引领者
  • 新版 UTS 2022 版:支持linux上访问同步sqlserver
  • 变脸、看完毒液3,我把自己的脸变毒液了,视频有点惊悚!请谨慎观看
  • Node.js-API 限流与日志优化
  • 线代的几何意义(2)——矩阵乘法,三维及更高维的解释,与非方阵的几何解释
  • FLINK单机版安装部署入门-1
  • 双十一买啥最划算?盘点2024年双十一必买好物!超全选购指南
  • 数据治理项目怎么做,3种推进思路可参考
  • 有哪些“极简风”页面设计的办公协同工具?再不怕眼花缭乱啦!
  • 同是正式编铁饭碗,央国企薪资待遇哪家高?
  • ChatGPT Search:AI 搜索离「谷歌杀手」还有多远?
  • 架构师之路-学渣到学霸历程-44
  • 快乐数算法
  • VC++获取指定进程的路径-支持32位和64位
  • 解决Android Studio 控制台中文乱码
  • R语言生物群落(生态)数据统计分析与绘图丨tidyverse数据清洗、多元统计分析、随机森林、回归及混合效应模型、结构方程模型等