六、Go语言快速入门之数组和切片

文章目录

    • 数组和切片
      • 数组
        • :one: 数组初始化
        • :two: 数组的遍历
        • :three: 多维数组
        • :four: 将数组传递给函数
      • 切片(Slice)
        • :one: 切片的初始化
          • :star: `new`和`make`区别
        • :two: 切片的使用
        • :three: 将切片传递给函数
        • :four: 多维切片
        • :four: Bytes包
        • :four: 切片和垃圾回收

📅 2024年4月28日
📦 使用版本为1.21.5

数组和切片

⭐️ 在go语言中数组和切片看起来几乎一模一样,区别在于数组是不可变扩容的,切片是可变可伸缩(在Java中想数组和列表(ArrayList))


数组

⭐️ 在go中数组只能是基本类型,不能是引用类型(在Java中两者都可以)

⭐️ 由于数组是固定大小的,如果你知道了你要存放数据的长度,且以后不会有扩容了,就可以考虑使用数组

⭐️ Go语言的数组是一种值类型,不是指针类型

1️⃣ 数组初始化

⭐️ 由于数组在声明后大小就不能变,声明大小时可以使用常量来替换

func main() {var a = 1;const b = 1;var aa [a]int; //报错a是变量var bb [b]int;
}

⭐️ 当然和其他语言一样它还可以声明并赋值,下标也是从0开始的,可以通过下标来范围和其他语言类似

func main() {const b = 3var bb = [b]int{1,2,3}println(bb[0])
}

⭐️ 可以通过len来访问数组中元素的数量,还可以通过cap来查看数组的容量

⭐️ 如果想要创建一个指针类型的数组就可以用到new

  1. 数值类型数组赋值,改变另外一个数组不会影响赋值的数组
func main() {a := [5]int{1, 2, 3, 4, 5}b := ab[2] = 100fmt.Println(a)fmt.Println(b)
}//输出
[1 2 3 4 5]
[1 2 100 4 5]
  1. 指针类型相反
func main() {a := new([5]int)b := ab[2] = 100fmt.Println(a)fmt.Println(b)
}//输出
&[0 0 100 0 0]
&[0 0 100 0 0]

⭐️ 还可以通过切割来访问数组的某一段数值,和其他语言差不多

arr[1:3]
2️⃣ 数组的遍历

⭐️ 可以使用forfor-range来遍历数组

func main() {a := []int{1, 2, 3, 4, 5}for i := 0; i <= len(a); i++ {fmt.Printf("%d ", i)}println()for i, v := range a {fmt.Printf("%d %d\n", i, v)}
}
3️⃣ 多维数组

⭐️ 想像成阵列就好了

func main() {var a [10][4]intfor i := 0; i < len(a); i++ {for j := 0; j < len(a[i]); j++ {a[i][j] = i + j}}
}

⭐️ 还可以使用for-range

func main() {var a [10][4]intfor row := range a {for column := range a[0] {a[row][column] = 1;}}
}
4️⃣ 将数组传递给函数

第一个大数组传递给函数会消耗很多内存,使用下面的方法可以避免:

  • 可以像C语言那样使用数组的指
  • 也可以使用切片(学到切片再说)
package mainimport "fmt"func main() {var a [10][4]intfuncName(&a)
}func funcName(a *[10][4]int) {for i := 0; i < len(a); i++ {for j := 0; j < len(a[i]); j++ {fmt.Printf("a[%d][%d] ", i, j)}}
}

切片(Slice)

⭐️ 切片在Go中使用的更加广泛,由于它可变成的特性,可以使用它来频繁的插入和删除元素

⭐️ 一个切片是对底层数组的一个连续片段的引用,因此它被视为引用类型。这种结构设计使得多个切片能够共享相同的底层数组内存,即使这些切片表达的是数组的不同部分。当通过一个切片对底层数组进行修改时,所有指向同一底层数组的其他切片都会看到这些更改。这是因为切片并没有复制底层数组的元素,而只是提供了一种访问和操作这些元素的方式,有点像java数组和ArrayList的关系,

⭐️ 一个切片由三个部分组成,指针、长度、容量,指针指向第一个切片元素对于的底层数组元素地址,但是并不一定就是数组的一个元素

⭐️ 优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片
比数组更常用

1️⃣ 切片的初始化

⭐️ 切片的初始化和数组类似,但是切片可以使用make关键字来创建,也推荐使用make来创建切片,make函数接收三个参数:类型,长度,容量;,容量一定是会大于或者等于长度的,就像一个桶子里面水的高度不会大于桶子的高度

Screenshot_2024_0429_230208

⭐️ 可以直接通过一个数组来创建切片Slice

arr1[0:5]表示此数组从0到5(不包含5)被切成了一个切片5就是此切片的len

如果是arr1[0:5:5] 前两个数和之前提到的一样,但是最后这个(5-0)就表示cap

func main() {arr1 := [10]int{1,2,3,4,5,6,7,8,9,10}var slice1 []int = arr1[0:5] //通过数组的切片创建fmt.Println(slice1) 
}

⭐️ 多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存
储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块

func main() {arr1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var slice1 []int = arr1[0:5] //通过数组的切片创建var slice2 []int = arr1[0:5]slice2[0] = 100 //修改切片2的0下标元素fmt.Println(slice1[0]) //发现切片1的也修改了
}//输出:
100

此时如果你想要扩容,就可以使用切片的切片操作,来扩容

然后,当执行 slice1 = slice1[0:6] 时,Go 语言会检查新切片所需的容量是否超过了现有切片的容量。在这个例子中,新切片需要的容量是6,而现有切片的容量是5。由于6大于5,Go 语言会分配一个新的底层数组来容纳这6个元素,并将新切片的指针指向这个新数组。然后,它会复制原切片中的元素到新数组中,并更新新切片的长度和容量。

因此,尽管最初的切片 slice1 的容量只有5,但是在执行 slice1 = slice1[0:6] 之后,slice1 被重新分配了一个更大的底层数组,其容量至少为6,以容纳新的切片范围。这就是为什么 slice1 可以扩展到 0:6 的原因

func main() {arr1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var slice1 []int = arr1[0:5] //通过数组的切片创建fmt.Println(slice1) //输出:[1 2 3 4 5]slice1 = slice1[0:6] //切片的切片操作扩容fmt.Println(slice1) //输出:[1 2 3 4 5 6]
}

⭐️ 通过var nums []int这种方式声明的切片,默认值为nil,所以不会为其分配内存,而在使用make进行初始化时,建议预分配一个足够的容量,可以有效减少后续扩容的内存消耗

//源代码// make内置函数分配并初始化一个类型的对象
// slice、map或chan(仅限)和new一样,第一个参数是类型,而不是
/ /值。与new不同,make的返回值类型与its相同
//参数,而不是指针。结果的具体情况取决于
//类型:
//
// slice: size指定了长度。切片的容量为
//等于它的长度可以向提供第二个整数参数
//指定不同的容量;它必须不小于
/ /长度。例如,make([]int, 0, 10)会分配一个底层数组
//返回长度为0、容量为10的切片
//由底层数组支持。
// map:一个空的map被分配了足够的空间来容纳
//指定元素的数量在这种情况下,大小可以省略
//分配一个较小的起始长度。
// channel:使用指定的参数初始化channel的缓冲区
//缓存容量。如果为零,或大小省略,则通道为
/ /无缓冲的。
func make(t Type, size ...IntegerType) Type

⭐️ 你使用make函数创建一个切片时,如果指定了长度(第二个参数),那么切片将会被初始化为该长度,并且每个元素都会被初始化为该类型对应的零值。对于整数类型(如int),零值就是0。

func main() {var a = make([]int, 1, 10)println(a[0]) //输出0
}

⭐️ 切片的长度代表着切片中元素的个数,切片的容量代表着切片总共能装多少个元素,切片与数组最大的区别在于切片的容量会自动扩张,而数组不会

⭐️ 和数组一样也可以使用new来创建数组

func main() {var a = make([]int, 10)var b = new([10]int)[0:10]fmt.Printf("%T", b)fmt.Printf("%T", a)
}
⭐️ newmake区别
  • new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{} 。

  • make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:切片mapchannel

    Screenshot_2024_0429_224147

2️⃣ 切片的使用

⭐️ 切片的基本使用与数组完全一致,区别只是切片可以动态变化长度

⭐️ 通过append来对切片实行操作

  1. 在尾部添加元素
func main() {var a = make([]int, 0, 0)          //创建一个空切片a = append(a, 1, 2, 3, 4, 5, 6, 7) //添加元素fmt.Println(len(a), cap(a))        //输出内部元素个数和切片的容量
}//输出7,8

slice 预留的 buffer容量 大小是有一定规律的。 在golang1.18版本更新之前网上大多数的文章都是这样描述slice的扩容策略的: 当原 slice 容量小于 1024 的时候,新 slice 容量变成原来的 2 倍;原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。 在1.18版本更新之后,slice的扩容策略变为了: 当原slice容量(oldcap)小于256的时候,新slice(newcap)容量为原来的2倍;原slice容量超过256,新slice容量newcap = oldcap+(oldcap+3*256)/4

  1. 在头部插入元素

⭐️ ...操作符被称为扩展操作符(spread operator),用于将切片或数组展开为多个单独的参数。

func main() {var a = make([]int, 0, 0)          //创建一个空切片a = append(a, 1, 2, 3, 4, 5, 6, 7) //添加元素a = append([]int{-1, 'a'}, a...)   //这个a会转换成unicode代码中的值97fmt.Println(a) //输出数组 [-1 97 1 2 3 4 5 6 7]fmt.Println(len(a), cap(a)) //输出内部元素个数和切片的容量
}
  1. 从中间插入
1.a[:i+1]: 这部分表示从切片a的开始到索引i+1处的元素,即保留原始切片的前i+1个元素。
2.[]int{999,888}: 这是一个包含两个整数999888的切片,这是要插入的元素。
3.a[i+1:]...: 这部分表示从切片a的索引i+1处开始到末尾的所有元素,采用...的方式表示这些元素将被展开并追加到前面的切片组合之后。
4.append(...):将前面提到的三个切片部分组合起来,并执行append操作,将所有元素添加到一起,最终生成一个新的切片。
5.a = ...: 将新生成的切片赋值回原始切片a,完成插入操作并修改原始切片。
通过这种方式,切片a在索引i处被扩展,其中插入了两个新的整数值999888func main() {i := 3var a = make([]int, 0, 0)          //创建一个空切片a = append(a, 1, 2, 3, 4, 5, 6, 7) //添加元素a = append(a[:i+1], append([]int{999, 888}, a[i+1:]...)...) //使用i当索引,在索引中间开辟两个空间来插入来插入元素fmt.Println(a)              //输出数组fmt.Println(len(a), cap(a)) //输出内部元素个数和切片的容量
}
  1. 删除元素
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
//从头部删除n个元素
nums = nums[n:]
fmt.Println(nums) //n=3 [4 5 6 7 8 9 10]//从尾部删除n给元素
nums = nums[:len(nums)-n]
fmt.Println(nums) //n=3 [1 2 3 4 5 6 7]//从中间指定下标删除
nums = append(nums[:i], nums[i+n:]...)
fmt.Println(nums)// i=2,n=3,[1 2 6 7 8 9 10]
  1. 将切片 b 的元素追加到切片 a 之后: a = append(a, b…)
  2. 复制切片 a 的元素到新的切片 b 上:
    b = make([]T, len(a))
    copy(b, a)
  3. 删除位于索引 i 的元素: a = append(a[:i], a[i+1:]…)
  4. 切除切片 a 中从索引 i 至 j 位置的元素: a = append(a[:i], a[j:]…)
  5. 为切片 a 扩展 j 个元素长度: a = append(a, make([]T, j)…)
  6. 在索引 i 的位置插入元素 x: a = append(a[:i], append([]T{x}, a[i:]…)…)
  7. 在索引 i 的位置插入长度为 j 的新切片: a = append(a[:i], append(make([]T, j), a[i:]…)…)
  8. 在索引 i 的位置插入切片 b 的所有元素: a = append(a[:i], append(b, a[i:]…)…)
  9. 取出位于切片 a 最末尾的元素 x: x, a = a[len(a)-1], a[:len(a)-1]
  10. 将元素 x 追加到切片 a: a = append(a, x)
3️⃣ 将切片传递给函数

⭐️ 前面说过传递数组你可以使用切片来提高程序运行效率

⭐️ 如果你有一个函数需要对数组做操作,你可能总是需要把参数声明为切片。当你调用该函数时,把数组分
片,创建为一个切片引用并传递给该函数

var slice1 []type = arr1[:] 那么 slice1 就等于完整的 arr1 数组(所以这种表示方式是
arr1[0:len(arr1)] 的一种缩写)。另外一种表述方式是: slice1 = &arr1 。
arr1[2:] 和 arr1[2:len(arr1)] 相同,都包含了数组从第二个到最后的所有元素。
arr1[:3] 和 arr1[0:3] 相同,包含了从第一个到第三个元素(不包括第三个)

func main() {var arr = [6]int{1, 2, 3, 4, 5, 6}sum := sum(arr[:]) //数组的切片fmt.Println(sum)
}func sum(a []int) int {sum := 0for _, i := range a {sum += i}return sum
}
4️⃣ 多维切片

⭐️和数组一样,切片通常也是一维的,但是也可以由一维组合成高维。通过分片的分片(或者切片的数
组),长度可以任意动态变化,所以 Go 语言的多维切片可以任意切分。而且,内层的切片必须单独分配
(通过 make 函数)

func main() {slice := [][]int{{1, 2, 3}, {4, 5, 6}}// slice2 := make([][]int, 2,10) 通过makefmt.Println(slice)
}
4️⃣ Bytes包

⭐️ []bytes这种切片类型很常见,包括使用的string类型,它底层就是使用的它

⭐️ 他提供一个非常有用的Buffer,它可以实现类似JavaStringbuilder的功能

//源代码
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {buf      []byte // contents are the bytes buf[off : len(buf)]off      int    // read at &buf[off], write at &buf[len(buf)]lastRead readOp // last read operation, so that Unread* can work correctly.
}

⭐️ 使用,感觉它没有Stringbuilder好用,它不能使用那种流方法,只能另起一行或者是用循环输入

func main() {var buffer bytes.Bufferbuffer.WriteString("hello") //追加Stringbuffer.WriteString("world") //追加Stringstr := buffer.String() //获取Stringfmt.Println(str) //helloworld}
func main() {var buffer bytes.Bufferfor {var str stringfmt.Scanln(&str)buffer.WriteString(str) //追加Stringif str == "q" {break}}str := buffer.String() //获取Stringfmt.Println(str)       //helloworld}
4️⃣ 切片和垃圾回收

⭐️ 切片的底层指向一个数组,该数组的实际体积可能要大于切片所定义的体积。只有在没有任何切片指向的
时候,底层的数组内层才会被释放,这种特性有时会导致程序占用多余的内存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/59278.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HFSS 3D Layout中Design setting各个选项的解释

从HFSS 3D LAYOUT菜单中&#xff0c;选择Design Settings打开窗口&#xff0c;会有六个选项&#xff1a;DC Extrapolation, Nexxim Options, Export S Parameters, Lossy Dielectrics, HFSS Meshing Method, and HFSS Adaptive Mesh. DC Extrapolation 直流外推 直流外推分为标…

大模型备案安全评估报告编写说明

一、语料安全评估 (一)评估内容 1.文本训练语料规模 训练语料存储规模&#xff0c;按文本格式存储时的语料大小。 训练语料数量&#xff0c;按词元(Token)计数。 2.各类型语料规模 训练语料中的中文文本、英文文本、代码、图片、音频、视频及其他语料的规模。 3.训练语料…

ElementUI<el-table></el-table>表格中固定列横向滚动条无法拖动解决

当表格有固定列会出现横向滚动条无法拖动问题&#xff0c;尤其是固定列固定在左边且表格没数据的时候。这可能是因为固定区域盖住了横向滚动条&#xff0c;不是视觉上的覆盖&#xff0c;是去拖动时没有触发效果。 解决方法如下&#xff1a; 1.修改层级 使用z-index&#xff0…

ctfshow web系列

声明: 本文章只适用于网络安全交流与学习,请读者遵循网络安全法,若读者从事一些与网络安全不相关的事情&#xff0c;结果均与本人无关&#xff01;&#xff01;&#xff01; 是ctfshow的web题:https://www.ctf.show/ web3: 开局提示使用php include get url include()函数是…

善用Git LFS来降低模型文件对磁盘的占用

将讲一个实际的例子&#xff1a;对于模型文件&#xff0c;动辄就是好几个G&#xff0c;而有的仓库更是高达几十G&#xff0c;拉一个仓库到本地&#xff0c;稍不注意直接磁盘拉满都有可能。 比如&#xff1a;meta-llama-3.1-8b-instruct&#xff0c;拉到本地后发现居然占用了60G…

十四届蓝桥杯STEMA考试Python真题试卷第二套第五题

来源:十四届蓝桥杯STEMA考试Python真题试卷第二套编程第五题 本题属于迷宫类问题,适合用DFS算法解决,解析中给出了Python中 map() 和列表推导式的应用技巧。最后介绍了DFS算法的两种常见实现方式——递归实现、栈实现,应用场景——迷宫类问题、图的连通性、树的遍历、拓朴排…

MYSQL安装(ubuntu系统)

rpm -qa 查询安装软件包 ps axj 查询服务 卸载mysql&#xff08;万不得已&#xff09; ps axj | grep mysql 查看是否存在mysql服务 systemctl stop mysqld 关闭该服务 rpm -qa | grep mysql 查安装mysql安装包 rmp -qa | grep mysql | xargs (yum apt) -y remove进行批量…

智能提醒助理系列-jdk8升级到21,springboot2.3升级到3.3

本系列文章记录“智能提醒助理”产品建设历程&#xff0c;记录实践经验、巩固知识点、锻炼总结能力。 本篇介绍技术栈升级的过程&#xff0c;遇到的问题和解决方案。 一、需求出发点 智能提醒小程序 当前使用的是jdk8&#xff0c;springboot2.3,升级到jdk21和springboot3.3 学…

MMdetection3D环境配置

为啥要写这个&#xff1f;&#xff1a;没看网上有这个全的环境配置教程T^T 首先说明一下&#xff0c;由于mmdetection3d官方文档说支持Linux平台&#xff0c;实验性支持windows&#xff0c;所以我们选择Linux平台进行开发 0.Linux双系统安装 参考这个视频即可 Windows 和 U…

openapi回调地址请求不通过

目录 1. 验证url接口get请求本地自测报错 2. 测试回调模式成功不返回结果 3. 测试回调模式返回结果带双引号 对接企业微信 产生会话回调事件 接口问题解决 1. 验证url接口get请求本地自测报错 java.lang.IllegalArgumentException: Last encoded character (before the pa…

Chrome和夸克谁更护眼

在当今数字化时代&#xff0c;我们每天长时间面对电脑和手机屏幕&#xff0c;眼睛的健康问题变得越来越重要。浏览器作为我们日常使用频率极高的工具&#xff0c;其护眼功能的优劣直接影响到我们的视觉舒适度。本文将对Chrome和夸克两款主流浏览器进行对比&#xff0c;探讨它们…

命令行参数、环境变量、地址空间

命令行参数&#xff1a; int main(int argc, char *argv[ ])&#xff0c;main的参数可带可不带。argc参数通常代表后面的char *argv的元素个数有多少。 在linux中会把输入的字符串存到char *argv[ ]中&#xff0c;在数组的结尾为NULL。 命令行参数可以让同一个程序可以通过不同…

基于Matlab 模拟停车位管理系统【源码 GUI】

系统对进入停车位的车辆进行车牌识别&#xff0c;将识别出来的车牌号显示出来&#xff1b;然后对车主进行人脸识别&#xff0c;框出车主照片的人脸部分作为车主信息的标记&#xff0c;记录在系统库中。车辆在库期间&#xff0c;系统使用者可以随意查看车辆与车主信息的获取过程…

SpringBoot项目集成ONLYOFFICE

ONLYOFFICE 文档8.2版本已发布&#xff1a;PDF 协作编辑、改进界面、性能优化、表格中的 RTL 支持等更新 文章目录 前言ONLYOFFICE 产品简介功能与特点Spring Boot 项目中集成 OnlyOffice1. 环境准备2. 部署OnlyOffice Document Server3. 配置Spring Boot项目4. 实现文档编辑功…

学习笔记:微服务技术栈(一)服务治理框架SpringCloud

教学视频链接&#xff1a; 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 目录 前言一、认识微服务1.1 服务架构1.2 微服务架构1.3 SpringCloud 二、服务拆分及远程调用2.1 服务拆分细节2.2 服务间调用 …

前端小练习——星辰宇宙(JS没有上限!!!)

前言&#xff1a;在刚开始学习前端的时候&#xff0c;我们会学习到前端三件套中的JavaScript&#xff0c;可能那时候读者没有觉得JavaScript这个语言有多么的牛逼&#xff0c;本篇文章将会使用一个炫酷的案例来刷新你对JavaScript这个语言的认知与理解。 ✨✨✨这里是秋刀鱼不做…

【网络】传输层协议TCP(下)

目录 四次挥手状态变化 流量控制 PSH标记位 URG标记位 滑动窗口 快重传 拥塞控制 延迟应答 mtu TCP异常情况 四次挥手状态变化 之前我们讲了四次挥手的具体过程以及为什么要进行四次挥手&#xff0c;下面是四次挥手的状态变化 那么我们下面可以来验证一下CLOSE_WAIT这…

XingHan-Team团队官网系统源码 全开源

XingHan-Team 官网程序是一个现代化的企业官网管理系统&#xff0c;由星涵网络工作室开发。 本系统提供了完整的网站内容管理功能&#xff0c;包括用户管理、内容发布、成员查询、成员申请等功能。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/8995…

vrrp和mstp,vrrp和byd

vrrp和mstp 思路 vrrp是用来虚拟网关&#xff0c;噢&#xff0c;是虚拟一条虚拟网关 优先级&#xff0c;priority越大越优先&#xff0c;优先级相同&#xff0c;哪个的路由器的vrrp先起来&#xff0c;谁就是主 mstp是快速生成树协议&#xff0c;防止环路用的 优先级越小越优…

图说复变函数论重大错误:将无穷多各异平面误为同一面

黄小宁 医学若将前所未知的“新冠”病毒误为已熟知的流感病毒&#xff0c;后果...&#xff1b;数学将前所未知的点集误为已熟知的集就会引出一连串的重大错误。 h定理&#xff1a;点集AB的必要条件是A≌B。 证&#xff1a;&#xff08;1&#xff09;任何图≌自己是几何学最起码…