首页 > 其他分享 >go切片与数组

go切片与数组

时间:2023-01-17 11:35:12浏览次数:55  
标签:nums int 切片 numsCopy 数组 go 长度

转载:Go基础系列:7. 内置集合 - 数组 - 知乎 (zhihu.com)

学到什么

  1. 什么是数组?
  2. 如何获取数组长度?
  3. 如何操作数组元素?
  4. 如何比较两个数组?
  5. 如何拷贝数组?
  6. 如何创建多维数组?
  7. 如何省略变量(补充)?

概念

数组是一个长度固定和元素类型相同的集合,类型也可以自定义。如果想保存任意类型,定义一个接口类型数组。

数组每个元素的位置,称为索引。第一元素索引为0,第二个为1,以此类推。

声明

声明格式: var 数组名 [长度]类型

举例:声明一个长度为 4,元素类型为 int 的数组。

var nums [4]int

// 打印
[0 0 0 0]

声明后每个元素默认初始化为 0 ,如果是字符串数组,默认初始化为空字符串。

初始化

数组有多种初始化方式,一一列举说明,以下我用 := 简写方式。

方式一:初始化每个元素,如果超过数组长度,编译时会提示越界错误。

nums := [4]int{3, 2, 1, 4}

方式二:初始化部分元素,只有索引 0 和 1 被赋值,后面的元素默认为 0 。

nums := [4]int{3, 2}

// 打印
[3 2 0 0]

方式三:使用 key/value 形式初始化指定索引元素,也可以和上面方式混合。

nums := [4]int{0: 3, 3: 4}
或
nums := [4]int{3, 3: 4}

// 打印
[3 0 0 4]

方式四:不指定数组长度,使用 ... 让编译器自己决定数组的长度。

// 数组长度为 4
nums := [...]int{3, 2, 1, 4}

获取数组的长度

使用内置的函数 len 获取数组的长度。

len(array)

还可以用于获取切片、map、字符串、通道的长度。

数组元素操作

1. 获取元素

使用索引获取数组的元素,后面要讲的切片获取方式也是如此。

nums := [...]int{3, 2, 1, 4}
fmt.Println(nums[1])

// 输出
2

2. 设置元素

当有了一个数组后,后面可以对此数组元素进行修改。

nums := [...]int{3, 2, 1, 4}
nums[1] = 3
fmt.Println(nums)

// 输出
[3 3 1 4]

遍历数组

使用“for 循环语句”遍历数组,上篇“流程控制”中有详细的“for 循环语句”讲解。

遍历数组有两种方式,第一种是“迭代计数”,第二种“for-range”,使用如下:

1. 迭代计数

nums := [...]int{3, 2, 1, 4}
for i := 0; i < len(nums); i++ {
    fmt.Println(nums[i])
}

2. for-range

for i, v := range nums {
   fmt.Println("索引:", i, " 值", v)
}

// 输出
索引: 0  值 3
索引: 1  值 2
索引: 2  值 1
索引: 3  值 4

数组比较

两个数组是否相等要考虑两方面:

  • 数组必须是同一类型,这块指的类型不是元素类型,而是数组长度和元素类型决定数组的类型,例如: [2]int{1, 2} 它的类型是 [2]int ,不要误以为是 array 。
  • 数组元素相等
a := [2]int{1, 2}
b := [2]int{1, 2}
if a == b {
    print("相等")
} else {
    print("相等")
}

// 输出
相等

注:如果 b 的初始化方式为 [...]int{1, 2} , a 和 b 依然是相等的,因为类型还是 [2]int 。

数组拷贝

在 Go 语言中,数组是值类型,也就是说在传递过程中会自动拷贝一份。

nums := [...]int{3, 2, 1, 4}
numsCopy := nums
numsCopy[1] = 3
fmt.Println("nums:", nums)
fmt.Println("numsCopy:", numsCopy)

// 输出
nums: [3 2 1 4]
numsCopy: [3 3 1 4]

多维数组

为了更好的理解多维数组,现在还原一个场景。

现在有间教室,里面有 4 行 3 列的座位,现在让你记录每个座位学生的状态,0表示旷课、1表示请假、2表示已到场。

1. 声明

先看看多维数组如何声明。

二维数组格式:

var 数组名 [长度][长度]类型 

三维数组格式:

var 数组名 [长度][长度][长度]类型 

依次类推可以继续声明四维数组、五维数组等等。

继续回到上面的场景,可以确定需要一个 4 行 3 列的二维数组。

var students [4][3]int

3. 初始化

设置每个学生的状态,可以看出 1 行 3 列学生旷课(值为0),3 行 2列学生请假(值为1)。

students := [4][3]int{
    {2, 2, 0},
    {2, 2, 2},
    {2, 1, 2},
    {2, 2, 2},
}

4. 遍历二维数组

使用“迭代计数”方式遍历 students 数组,输出旷课的学生座位。

// collection/mult-array.go

for i := 0; i < 4; i++ {
    for j := 0; j < 3; j++ {
        if students[i][j] == 0 {
            fmt.Printf("%d行%d列学生旷课", i+1, j+1)
        }
    }
}

// 输出
1行3列学生旷课

省略变量

在 Go 语言中,使用变量有个要求:“在函数体内,声明了变量就必须使用”。如果不想使用,就使用“下划线(_)”去省略。

举两个例子,省略函数返回的 b 变量和数组循环的索引 i 。

a, b := fun1()

for i, v := range array {
    ...
}

// 省略后
a, _ := fun1()
for _, v := range array {
    ...
} 

转载:Go基础系列:8. 内置集合 - 切片 - 知乎 (zhihu.com)

学到什么

  1. 什么是切片?
  2. 如何创建切片?
  3. 如何获取切片长度和容量?
  4. 切片和数组的关系?
  5. 操作切片具体元素?
  6. 切片元素如何追加和移除?
  7. 切片是引用类型还是值类型?
  8. 如何拷贝切片?
  9. 如何创建多维切片?
  10. 切片字符串是啥?

概念

切片使用起来类似长度可变的数组,不像数组长度是固定的。但切片的底层使用的还是数组,切片只是保存了对数组的引用,帮着管理数组,实现可变的效果。

声明

格式: var 切片名称 []数据类型

和数组声明的区别是,是否指明了长度,没有长度则为切片。

var nums []int

注:切片未初始化默认为 nil ,长度为 0 。如果清空切片可以赋值 nil,例: nums = nil 。

初始化

1. make 函数

使用 make 函数初始化切片,容量(马上有讲)参数可以省略,省略后长度和容量相等,格式如下:

切片名称 := make([]数据类型,长度,容量)

示例:

// 长度为 2,容量为 5
nums := make([]int, 2, 5)

下图是示例代码的原理图,下来深层了解切片。

蓝色区域为切片的结构,它包含数组的指针(ptr)、切片长度(len)和切片容量(cap)。

  • ptr:数组指针,保存数组的内存地址,指向数组的具体索引。
  • len:切片的长度,可以使用 len(nums) 函数获取,表示从指针对应的索引位置开始所使用的长度。
  • cap:切片的容量,可以使用 cap(nums) 函数获取,表示引用数组的长度。

黄色区域为切片底层引用的数组,数组的长度就是切片的容量。

2. 初始化具体值

nums := []int{1, 2, 3}

初始化了一个长度为 3 的切片,此时容量也为3。

操作具体元素

切片中元素的具体操作和数组的方式是一样的。如果获取元素时超出切片长度,即使没有超出容量,编译器也会报错。

nums := []int{1, 2, 3}

// 设置索引 1 的元素为 4
nums[1] = 4

fmt.Println(nums[1])

// 输出
4

获取子集

定义了一个切片或数组后,可以获取其中的一部分,即子集。

格式: 切片或数组[开始索引:结束索引]

获取从“开始索引”到“结束索引”的子集,包含开始索引,但不包含结束索引。如果是数组获取子集后,类型会转化为切片类型。

// 切片
nums := []int{1, 2, 3, 4, 5}
// 获取切片子集
nums1 := nums[2:4]   // []int{3, 4}

// 数组
arr := [5]int{1, 2, 3, 4, 5}
// nums2 为切片类型
nums2 := arr[2:4]    // []int{3, 4}

省略索引

“开始索引”和“结束索引”都可以省略。

  • 开始索引省略,表示子集从索引 0 开始到结束索引。
  • 结束索引省略,表示子集从开始索引到最后结束。
  • 都省略,如果是切片两者一样,如果是数组会转化为切片类型。
nums := []int{1, 2, 3, 4, 5}

// 开始索引省略
nums1 := nums[:3] // []int{1, 2, 3}

// 结束索引省略
nums2 := nums[2:] // []int{3, 4, 5}

// nums3 和 nums 相同
// 等价于:nums3 := nums
nums3 := nums[:]

如果代码中 nums 为数组,那 nums[:] 会将数组转化为切片。下来看看以上代码的原理图,这样会加深理解。

从图中可以看出所有的切片都指向同一个数组,这也说明了切片是一个引用类型,它在传递时不会进行拷贝。

追加和移除元素

往切片中追加元素,使用到 append 函数,此函数只能追加到切片末尾。

// collection/slice-append.go

nums := []int{1, 2, 3}
nums = append(nums, 2)
nums = append(nums, 4, 5)

fmt.Println(nums)

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

如果想追加到切片开头,没有原生的函数,使用 append 变向的实现,这种方式其实就是合并两个切片。

// collection/slice-append-front.go

nums := []int{1, 2, 3}
// ... 三个点表示将切片元素展开传递给函数
nums = append([]int{4}, nums...)

fmt.Println(nums)

// 输出
[4 1 2 3]

如何移除某个元素呢,使用切片子集和 append 函数变向实现。

// collection/slice-append-remove.go

nums := []int{1, 2, 3, 4, 5}
// 移除索引为 2 的元素值 3
nums = append(nums[:2], nums[3:]...)

fmt.Println(nums)

// 输出
[1 2 4 5]

切片拷贝

1. copy 函数

从上面了解到切片是一个引用类型,因此不能像数组一样直接赋值给一个新变量就会产生拷贝。下来使用 copy 函数完成切片的拷贝。

// collection/slice-copy-1.go

// 将 nums 拷贝到 numsCopy 
nums := []int{1, 2, 3}
numsCopy := make([]int, 3)

copy(numsCopy, nums)

// 修改了 numsCopy,不会对 nums 产生影响
numsCopy[0] = 2

fmt.Println("nums:", nums)
fmt.Println("numsCopy:", numsCopy)

// 输出
nums: [1 2 3]
numsCopy: [2 2 3]

numsCopy 长度可以小于或大于 nums 的长度,如果小于就会拷贝nums 前面一部分,大于会保留 numsCopy 后面一部分。

// collection/slice-copy-2.go

// numsCopy 长度小于 nums
nums := []int{1, 2, 3}
numsCopy := make([]int, 2)
// 前面两个元素 1 和 2 被复制
copy(numsCopy, nums)
fmt.Println("numsCopy(小于):", numsCopy)

// numsCopy 长度大于 nums
numsCopy = []int{4, 5, 6, 7}
// 4,5,6 会被覆盖,保留 7
copy(numsCopy, nums)
fmt.Println("numsCopy(大于):", numsCopy)

// 输出
numsCopy(小于): [1 2]
numsCopy(大于): [1 2 3 7]

2. “长度 > 容量”触发拷贝

使用 append 函数给切片追加元素时,如果追加的长度大于切片的容量时,切片的底层数组空间则重新开辟一块比原来大的地方,并把原来的数组值拷贝一份。

注解:

  • 图中”新数组“两个位置就是切片长度大于容量的时刻,这两个时刻会自动开辟新数组,与原来的数组没有任何关联,只是把值拷贝了一份。
  • 图中创建”新数组“时,容量的大小是原来的 2 倍,但这不是一成不变的,不同情况算法也会不一样,想要了解清楚我推荐一篇文章《深度解密Go语言之Slice》。

多维切片

这块和多维数组是类似的,唯一的不同点是切片没有指明长度,举个例子:

// 声明二维切片
var mult [][]int

// 初始化二维切片
students := [][]int{
    {2, 2, 0},
    {2, 2, 2},
    {2, 1, 2},
    {2, 2, 2},
}

注:如果想创建三维切片、四维切片,只要和多维数组类比就行。

切片字符串

这个是啥呢?是字符串可以使用上面的子集用法,来获取字符串中的一部分。

str := "I'm laomiao."
fmt.Println(str[4:7])

// 输出
lao

 new() 和 make() 的区别

看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。

  • new(T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 *T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{}
  • make(T) 返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:切片、map 和 channel

 

标签:nums,int,切片,numsCopy,数组,go,长度
From: https://www.cnblogs.com/dadishi/p/17057436.html

相关文章

  • Golang 跨平台编译
    CGO是什么cgo是在Android和iOS上运行Go程序的关键,它允许GO程序与C语言库相互操作未用到CGO的时候,建议编译的时候禁用CGO,比如编译ARM架构的时候就需要打开该选项......
  • 03-Django命令 python manage.py runserver
    使用Django开发,一定离不开这条命令,pythonmanage.pyrunserver[127.0.0.1:8000],这条命令是启动DJango,接下来就可以通过浏览器发起请求了。Django肯定也会遵守WSGI协议......
  • qt 按键数组
    按键配置QPushButton*controller_button_[APP_NUM_OF_CONTROLLER];for(inti=0;i<APP_NUM_OF_CONTROLLER;i++){controller_button_[i]=ne......
  • Golang如何实现节假日不打扰用户?
    ​1、场景想象下以下场景,嘿嘿...!一个iphone用户,闹钟是可以按节假日不响的!每日新闻机器人,节假日是可以不打扰我的!我的业务,节假日是可以...2、思路要实现识别节假日......
  • (13)go-micro微服务公用函数开发
    目录一前言二SwapTo通过jsontag进行结构体赋值三UserForResponse类型转化四最后一前言在utils目录中新建一个swap.go文件swap.go中主要存放一些可以使用到......
  • golang: os.open函数
    Open函数/*类似c里的fopen函数返回一个file的指针传入参数文件名(路径)*/funcOpen(namestring)(*File,error){/*调用OpenFile函数,传......
  • 代码随想录算法训练营第六天 | 哈希表理论基础,242.有效的字母异位词,349. 两个数组的交
    第五天周日休息~【提醒补坑:链表总结还没写】一、参考资料哈希表理论基础文章连接:https://programmercarl.com/%E5%93%88%E5%B8%8C%E8%A1%A8%E7%90%86%E8%AE%BA%E5%......
  • Go语言template相关知识
    templeate三板斧创建模版:模版文件以.tmpl结尾,文件的语法和html相同。解析模版t,err:=template.ParseFile("./filename.tmpl")iferr!=nil{ //错误处理......
  • JS数组对象 | 中文按照首字母排序sort()、localeCompare()
    一、数组//根据中文の首字母排序letarr=['上海','北京','广州','深圳']arr.sort((a,b)=>a.localeCompare(b))console.log(arr)//数组sort()方法是会改变原......
  • golang:osd.read函数
    函数原型func(f*File)Read(b[]byte)(nint,errerror){//传入b一段内存Buff//检测file是否为空,为空则返回 iferr:=f.checkValid("read"......