六、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 直流外推 直流外推分为标…

ArcGIS Pro SDK (二十一)渲染

ArcGIS Pro SDK (二十一)渲染 文章目录 ArcGIS Pro SDK (二十一)渲染1 定义唯一值呈现器定义2 为最新观测值设置唯一值渲染器3 为先前的观测值设置唯一值渲染器4 设置简单的渲染器以绘制轨迹线5 检查先前的观测值和轨道线可见性6 使轨迹线和先前的观测点可见7 检索当前观测…

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

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

Shiro权限刷新

一、权限刷新的需求背景 在实际应用中&#xff0c;用户的权限可能会因为多种原因而发生变化&#xff0c;如用户角色的调整、权限的授予或撤销等。如果系统不能及时地反映这些变化&#xff0c;就可能导致安全漏洞或功能受限。因此&#xff0c;Shiro框架提供了权限刷新的机制&am…

sqlserver循环的学习

最近在测试项目功能时&#xff0c;需要生成一些随机的问题总数、改善数、改善率的数据&#xff0c;因此需要用到SqlServer的循环&#xff0c;在这里记录一下。下面以随机生成十条数据为例&#xff0c;表结构如下: CREATE TABLE re_class(guid NVARCHAR(50) primary key,totalC…

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. 实现文档编辑功…

SpringBoot开箱即用

点开SpringBoot项目POM文件中父依赖,按住Ctrl+鼠标左键,点进去 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version><relativePath/&…

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

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

Centos 下安装 jsoncpp 库、bundle 数据压缩库、httplib 库 -- 详细教程

一、安装 jsoncpp 库 sudo yum install epel-release sudo yum install jsoncpp-devel [xyllocalhost ~]$ ls /usr/include/jsoncpp/json/ assertions.h config.h forwards.h reader.h version.h autolink.h features.h json.h value.h writer.h 注意&#xff1a;Centos 版本不…