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

news2024/11/23 7:01:57

文章目录

    • 数组和切片
      • 数组
        • :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 = 3
	var bb = [b]int{1,2,3}
	println(bb[0])
}

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

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

  1. 数值类型数组赋值,改变另外一个数组不会影响赋值的数组
func main() {
	a := [5]int{1, 2, 3, 4, 5}
	b := a
	b[2] = 100
	fmt.Println(a)
	fmt.Println(b)
}

//输出
[1 2 3 4 5]
[1 2 100 4 5]

  1. 指针类型相反
func main() {
	a := new([5]int)
	b := a
	b[2] = 100
	fmt.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]int
	for 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]int
	for row := range a {
		for column := range a[0] {
			a[row][column] = 1;
		}
	}
}
4️⃣ 将数组传递给函数

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

  • 可以像C语言那样使用数组的指
  • 也可以使用切片(学到切片再说)
package main

import "fmt"

func main() {
	var a [10][4]int
	funcName(&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代码中的值97
	fmt.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 := 3
	var 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 := 0
	for _, 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) 通过make
	fmt.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.Buffer
	buffer.WriteString("hello") //追加String
	buffer.WriteString("world") //追加String
	str := buffer.String() //获取String
	fmt.Println(str) //helloworld

}
func main() {
	var buffer bytes.Buffer
	for {
		var str string
		fmt.Scanln(&str)
		buffer.WriteString(str) //追加String
		if str == "q" {
			break
		}
	}
	str := buffer.String() //获取String
	fmt.Println(str)       //helloworld

}
4️⃣ 切片和垃圾回收

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

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

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

相关文章

【Sublime Text】格式化Json和XML

无package control解决方案 删除文件中的package control这一行并保存 下载 下载中

vue常用的修饰符有哪些

1、修饰符是什么 在Vue 中&#xff0c;修饰符处理了许多 DOM 事件的细节&#xff0c;让我们不再需要花大量的时间去处理这些烦恼的事情&#xff0c;而能有更多的精力专注于程序的逻辑处理 vue中修饰符分为以下五种 汇总修饰符说明表单lazy光标离开标签的时候&#xff0c;才会…

微服务设计模式 - 发布订阅模式(Publisher Subscriber Pattern)

微服务设计模式 - 发布订阅模式&#xff08;Publisher Subscriber Pattern&#xff09; 定义 发布-订阅模式&#xff08;Publisher-Subscriber Pattern&#xff09;是一种常见的设计模式&#xff0c;被广泛用于云计算和分布式系统中&#xff0c;以实现松散耦合的组件间通信。发…

00-开发环境 MPLAB IDE 配置

MPLAB IDE V8.83 File 菜单简介 New (CtrlN)&#xff1a; 创建一个新文件&#xff0c;用于编写新的代码。 Add New File to Project...&#xff1a; 将新文件添加到当前项目中。 Open... (CtrlO)&#xff1a; 打开现有文件。 Close (CtrlE)&#xff1a; 关闭当前打开的文件。 …

Pytorch猴痘病识别

Pytorch猴痘病识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&#xff1a;jupyte…

GA/T1400视图库平台EasyCVR视频分析设备平台微信H5小程序:智能视频监控的新篇章

GA/T1400视图库平台EasyCVR是一款综合性的视频管理工具&#xff0c;它兼容Windows、Linux&#xff08;包括CentOS和Ubuntu&#xff09;以及国产操作系统。这个平台不仅能够接入多种协议&#xff0c;还能将不同格式的视频数据统一转换为标准化的视频流&#xff0c;通过无需插件的…

Kafka相关知识点(上)

为什么要使用消息队列&#xff1f; 使用消息队列的主要目的主要记住这几个关键词:解耦、异步、削峰填谷。 解耦: 在一个复杂的系统中&#xff0c;不同的模块或服务之间可能需要相互依赖&#xff0c;如果直接使用函数调用或者 API 调用的方式&#xff0c;会造成模块之间的耦合…

qt QTextEdit详解

QTextEdit是Qt框架中的一个文本编辑控件类&#xff0c;它提供了丰富的功能用于编辑和显示纯文本以及富文本。 重要方法 setPlainText(const QString &text)&#xff1a;设置纯文本内容。toPlainText()&#xff1a;获取纯文本内容。setHtml(const QString &text)&#…

大学城水电管理系统开发:Spring Boot指南

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

Github 2024-11-02 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2024-11-02统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目2Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero Genera…

【android12】【AHandler】【4.AHandler原理篇ALooper类方法全解】

AHandler系列 【android12】【AHandler】【1.AHandler异步无回复消息原理篇】-CSDN博客 【android12】【AHandler】【2.AHandler异步回复消息原理篇】-CSDN博客 【android12】【AHandler】【3.AHandler原理篇AHandler类方法全解】-CSDN博客 其他系列 本人系列文章-CSDN博客…

【深度学习】CrossEntropyLoss需要手动softmax吗?

【深度学习】CrossEntropyLoss需要手动softmax吗&#xff1f; 问题&#xff1a;CrossEntropyLoss需要手动softmax吗&#xff1f;答案&#xff1a;不需要官方文档代码解释 问题&#xff1a;CrossEntropyLoss需要手动softmax吗&#xff1f; 之前用 pytorch 实现自己的网络时&…

EtherCAT转ModbusTCP相关技术

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 MS-GW15 概述 MS-GW15 是 EtherCAT 和 Modbus TCP 协议转换网关&#xff0c;为用户提供一种 PLC 扩展的集成解决方案&#xff0c;可以轻松容易将 Modbu…

使用Centos搭建Rocket.Chat教程

本章教程,主要介绍如何在CentOS系统上搭建Rocket.Cha。 一、Rocket.Chat是什么? Rocket.Chat 是一个开源的团队协作和通讯平台,类似于 Slack 或 Microsoft Teams。它提供了即时消息、视频会议、文件共享、以及与其他服务的集成等功能。用户可以在自己的服务器上部署 Rocket.…

jenkins 构建报错 mvn: command not found

首先安装过 maven&#xff0c;并且配置过环境变量 win r ,输入 cmd 键入 mvn -v 出现上图输出&#xff0c;则证明安装成功。 原因 jenkins 没有 maven 配置全局属性, 导致无法找到 mvn 命令。 解决方案 找到全局属性&#xff0c;点击新增&#xff0c;配置 MAVEN_HOME 路…

C++入门基础知识134—【关于C 库函数 - gmtime()】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 库函数 - gmtime()的相关内容&#xf…

如何开发一个摄影行业小程序?开发一个摄影行业小程序需要哪些功能?南昌各行业小程序开发

如何开发一个摄影行业小程序&#xff1f;开发一个摄影行业小程序需要以下步骤&#xff1a; 1、需求分析&#xff1a;明确小程序的定位和功能需求&#xff0c;例如拍照、修图、分享、预约摄影师等。 2、设计界面&#xff1a;根据需求分析&#xff0c;设计小程序的用户界面&…

虚幻引擎5(UE5)学习教程

虚幻引擎5&#xff08;UE5&#xff09;学习教程 引言 虚幻引擎5&#xff08;Unreal Engine 5&#xff0c;简称UE5&#xff09;是Epic Games开发的一款强大的游戏引擎&#xff0c;广泛应用于游戏开发、影视制作、建筑可视化等多个领域。UE5引入了许多先进的技术&#xff0c;如…

linux线程的认识

1.虚拟地址空间 管理进程的pcb结构体task_struct中有一个mm_struct指针&#xff0c;指向虚拟地址空间&#xff0c;而编译好的代码中有地址&#xff0c;但是是虚拟地址&#xff0c;那么虚拟地址是怎么转化成真实的物理地址呢&#xff1f;通过页表转化 我们知道&#xff0c;代码…

05 Django 框架模型介绍(一)

文章目录 1、Django 模型简介2、Django 中创建并使用模型&#xff08;1&#xff09;新加一个名为 myapp 的应用&#xff08;2&#xff09;定义模型类&#xff08;2&#xff09;激活模型类&#xff08;3&#xff09;创建数据库迁移文件&#xff08;4&#xff09;应用迁移文件 3、…