Go语言成长之路(二)-内建容器(多图文示例)

1、数组

1.1、一维数组

一维数组的声明方式:

var variable_name [SIZE] variable_type

例如:

var b [5]int

数组初始化:

var b =  [5]int{1000, 2, 3, 7, 5}

如果数组长度不确定,可以使用 … 代替数组的长度,编译器会根据元素个数自行推断数组的长度:

var b =  [...]int{1,2,3,4,5}

如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
在这里插入图片描述

可以通过在数组名后接中括号加下标,访问数组元素
在这里插入图片描述

1.2、多维数组

数组声明:

var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type

例如:

var b [5][5]int

多维数组初始化:
在这里插入图片描述

1.3、数组参数传递

数组作为参数传递,会拷贝数组,属于值传递,函数内部对数组的修改不生效,要想修改数组,可以使用指针传递(但是注意,go语言中的指针和c语言中的不一样,不能运算)
在这里插入图片描述

2、Slice-切片

Go 数组的长度不可改变,在许多场景下使用起来十分不方便,因此,Go语言中提供了一种灵活的“动态数组”使用方式,被称为“切片”。

与数组相比,切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

2.1、切片定义

var identifier []type

例如:

var testSliceOne []int

这和数组的声明方式是一样的
切片定义的时候不需要声明长度,因为切片属于动态数组,当然也可以去指定

2.2、切片初始化

testSlice = arr[startIndex:endIndex] 

切片将会从arr数组的startIndex开始获取数字,然后到endIndex之前截止

在这里插入图片描述

2.3、切片常用操作

如下图所示
使用len操作获取切片长度
使用cap操作获取切片可扩展容量
切片的长度是内值的数量
切片的可扩展容量是切片所指向的目标数组从startIndex之后的数组长度
切片在赋值之前为nil,此时切片的len和cap均为0
在这里插入图片描述

如下图所示
使用append操作可以为切片添加一个或多个元素
使用copy操作可以将一个切片的内容拷贝到第二个切片
在这里插入图片描述
等等,为什么testSliceTwo为空?不是已经copy了吗?
其实这是copy的一个坑,如果进行切片复制,我们需要需要使用copy(dst, src)函数
但是copy实际复制的元素个数,是从两个切片的大小中取最小值,即min(len(dst),len(src)),如果len(dst)=0则没有办法完成复制。

因此,当我们创建一个需要接收复制的切片的时候,根据需要复制的个数,需要对切片的大小进行设置,如下图所示,使用make命令创建切片,指定长度大小,复制成功,这里的复制只是值的复制,这个时候sliceTwo切片已经不是指向arrayOne数组了

请添加图片描述

需要注意的是,在go语言中,切片相当于底层数组的一个视图,所以当你修改指向数组的切片的值的时候,原本数组的值也会被改变。
请添加图片描述

3、Map-集合

3.1、Map创建

/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)

/* 直接赋值 */
map_variable := map[key_data_type]value_data_type{
	"key_data" = value_data,
}

代码示例:
在这里插入图片描述

3.2、Map赋值

首先,我们直接赋值
在这里插入图片描述
发现报错了,错误信息为panic: assignment to entry in nil map

这是为什么呢?

golang中map是引用类型,应用类型的变量未初始化时默认的zero value是nil。直接向nil map写入键值数据会导致运行时错误。如上图所示,0在声明testMapOne后并未初始化它,所以它的值是nil, 不指向任何内存地址。需要通过make方法分配确定的内存地址。程序修改后即可正常运行

在这里插入图片描述
这样就成功赋值了。

3.3、Map获取

在这里插入图片描述
可以直接用类似数组取值的方式对Map进行取值

除此之外,这种取值方式还能够获得一个变量,如下图所示,第二个返回变量是一个bool值,代表着map当中是否存在与这个key相对应的value,用来做条件判断很方便
在这里插入图片描述

3.4、Map遍历

仍然是使用range关键字,可以让下图中的cityName在testMapOne中的所有key的范围内遍历
在这里插入图片描述
然而当我们第二次执行的时候,却发现遍历打印value的顺序不一样了
在这里插入图片描述
这是为什么呢?

这里我们参考了 Go range实现原理及性能优化剖析 中的示例

range是Golang提供的一种迭代遍历手段,可操作的类型有数组、切片、Map、channel等。

range在遍历Map的时候,获取迭代器是通过调用了mapiterinit()方法,如下图源码所示

// The loop we generate:
//   var hiter map_iteration_struct
//   for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
//           index_temp = *hiter.key
//           value_temp = *hiter.val
//           index = index_temp
//           value = value_temp
//           original body
//   }

而在mapiterinit方法里,有取随机数的部分。go语言会提前取一个随机数,把桶的遍历顺序随机化。

为什么要这么做呢?

map的本质是散列表,而map的增长扩容会导致重新进行散列,这就可能使map的遍历结果在扩容前后变得不可靠,Go设计者为了让大家不依赖遍历的顺序,每次遍历的起点–即起始bucket的位置不一样,即不让遍历都从bucket0开始,所以即使未扩容时我们遍历出来的map也总是无序的。

3.5、Map删除

调用delete方法进行键的删除

	delete(map_name,key_data)

如下图所示
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值