写点什么

Go 语言学习笔记:数组

用户头像
worry
关注
发布于: 2021 年 03 月 20 日
Go语言学习笔记:数组

大家好,今天我们聊聊 Go 语言中的数组。


Go 语言中有 3 种内建的数据结构可以让用户管理集合数据:数组(Array)、切片(Slice)、映射(Map)。其中数组又是切片和映射的基础数据结构。


概要

本部分内容包括:

  • 数组的内部实现

  • 数组的声明和初始化

  • 使用数组

  • 多维数组

  • 在函数间传递数组


内部实现

数组是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的元素。Go 语言中,数组的长度是固定的。存储的元素可以是内置数据类型,也可以是用户自定义数据类型。

数组存储相同类型的数据,占用连续的内存空间。由于内存连续,CPU 能把正在使用的数据缓存更久的时间。而且内存连续很容易计算索引,可以快速迭代数组中的所有元素。由于相同数据类型,有了首地址后,可以根据下标计算元素的存储地址,根据下标随机访问的效率很高,时间复杂度为 O(1)。相同数据类型,连续存储,就可以以固定速度索引数组中的任意数据,速度非常快。


声明和初始化

声明数组需要指定类型和数组长度

var weeks [7]int
复制代码

以上我们声明了一个数组 weeks,但是我们还没有对它进行显示初始化。在 Go 语言中声明变量时,总会使用对应类型的零值来对变量进行初始化。数组声明时,默认数组内每个元素都会初始化为对应类型的零值。这时候数组 weeks 里面的值是 5 个 0,这和 java 不一样,java 里 weeks 是 null。


声明后,可以对数组进行一次显示初始化,如下:

var weeks [7]intweeks = [7]int{0, 1, 2, 3, 4, 5, 6}
复制代码

我们也可以在数组声明时,使用数组字面量初始化数组,如下:

var weeks = [7]int{0, 1, 2, 3, 4, 5, 6}
复制代码

一旦声明,数组的类型和长度就不能改变了。如果要存储更多的数据就要重新创建一个更长的数组,再把原来的复制到新数组。

var weeks = [7]int8{0,1,2,3,4,5,6,7} //编译报错
复制代码

在函数内部,也可以使用:=短变量声明操作符

weeks := [7]int{0, 1, 2, 3, 4, 5, 6}
复制代码

我们也可以使用...让 Go 语言自动计算声明数组的长度

weeks := [...]int{0, 1, 2, 3, 4, 5, 6}
复制代码

如果只想初始化特定元素的值,可以

weeks := [7]int{1:1, 6:6}// 与weeks := [7]int{0,1,0,0,0,0,6}一样效果weeks := [...]int{1:1, 6:6} // 推断数组长度为7
复制代码

也可以初始化部分

weeks := [7]int{1,2,3}// 与weeks := [7]int{1,2,3,0,0,0,0}一样效果
复制代码

使用数组

由于数组存储相同类型的元素,占用连续的内存空间。因此数组可以根据下标随机访问,效率很高,操作符为[]。

weeks := [7]int{0, 1, 2, 3, 4, 5, 6}
复制代码

上面声明了一个 weeks 数组,可以通过下标访问数组元素

sunday := weeks[0]
复制代码

修改数组元素的值也很简单

 weeks[0] = 7
复制代码


Go 语言中,数组是一个值。意味着数组可以用在赋值操作中

var students1 [5]stringvar students2 = [5]string{"xiaoming","xiaohua","xiaoli","xiaoxiao","xiaoxi"}// 把students2的值赋值给students1students1 = students2fmt.Printf("students1:%v %p \n",students1,&students1)fmt.Printf("students2:%v %p \n",students2,&students2)
复制代码

只有类型和长度都相同的数组间才可以执行赋值操作。以上,Printf 是打印出格式化的字符串,%p 表示指针,十六进制表示,前缀 0x,输出结果如下:

students1:[xiaoming xiaohua xiaoli xiaoxiao xiaoxi] 0xc0000c2000 students2:[xiaoming xiaohua xiaoli xiaoxiao xiaoxi] 0xc0000c2050
复制代码

内存地址不一样,赋值操作相当于复制了一份数据


我们也可以声明所有元素都是指针的数组

var pArr [5]*intpArr = [5]*int{1:new(int),3:new(int)}fmt.Printf("pArr:%v \n",pArr)
复制代码


以上输出结果为:

pArr:[<nil> 0xc000120000 <nil> 0xc000120008 <nil>]
复制代码

复制指针数组,只会复制指针的值,而不会复制指针指向的值。


通过切片操作会产生一个新数组

var colors2 = [3]string{"red","yellow","blue"}var colors3 = colors2[1:2]fmt.Printf("colors3:%v \n",colors3)
复制代码

可以通过内建函数 len 获取数组的长度

clen := len(colors2)fmt.Printf("colors2 len:%v \n",clen)
复制代码

多维数组

多维数组用于管理具有父子关系的数据或者坐标相关联的数据。

// 声明并初始化一个直角三角形var triangle [3][2]float32triangle = [3][2]float32{{0,3.0},{4.0,0},{0,0}}fmt.Printf("triangle:%v \n",triangle)
// 声明并初始化一个等腰直角三角形triangle2 := [3][2]float32{{0,1.0},{-1.0,0},{1.0,0}}fmt.Printf("triangle2:%v \n",triangle2)
// 声明并初始化一个三角形的一条边triangle3 := [3][2]float32{1:{1.5,1.5},2:{-1.5,-1.5}}fmt.Printf("triangle3:%v \n",triangle3)
//访问与修改a := triangle2[0]fmt.Printf("a:%v \n",a)
ay := triangle2[0][1]fmt.Printf("ay:%v \n",ay)
triangle2[0] = [2]float32{0,0.9}fmt.Printf("triangle2:%v \n",triangle2)
triangle2[0][1] = 0.8fmt.Printf("triangle2:%v \n",triangle2)
复制代码


在函数间传递数组

Go 语言在函数间传递变量时,总是以值的方式传递。变量是数组也不例外,会复制数组并传递给函数。函数间传递数组是个开销很大的操作。

// 声明一个需要8MB的数组var array [1e6]int
// 将数组传递给foo,效率低下foo(array)func foo(array [1e6]int) { fmt.Println("foo")}
复制代码

更有效的做法,将数组指针传递给 foo,但是这样会共享内存。

foo2(&array)func foo2(array *[1e6]int) {   fmt.Println("foo2")}
复制代码


总结

  1. 数组用一组连续的内存空间存储相同类型的元素,数组的长度是固定的。数组按下标随机访问的效率很高,时间复杂度为 O(1)。虽说数组结构简单,但是它是切片和映射的基础数据结构。

  2. 声明数组需要指定类型和数组长度。数组声明时,默认数组内每个元素都会初始化为对应类型的零值,可以用数组字面量对数组进行自定义初始化,初始化后数组长度不能改变。

  3. 可以通过数组下标访问和修改数组中的元素。数组是值,相同类型相同长度的数组间可以进行赋值,赋值会执行复制操作,产生相同的 2 份数据。

  4. Go 语言在函数间传递变量时,总是以值的方式传递。直接传递数组开销比较大,有效的做法是传递数组指针给函数,但需要注意共享内存。


参考

  1. Go 语言实战


发布于: 2021 年 03 月 20 日阅读数: 10
用户头像

worry

关注

心若有翼,我自飞翔 2018.01.25 加入

一个戴着眼睛的肥胖的扑腾蛾子

评论

发布
暂无评论
Go语言学习笔记:数组