在PHP语言中,数组是动态的,是一个强大到变态的数据结构,说万物皆可数组化毫不过分。
但在Go语言中,数组是固定的,它是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。由于数组长度的固定,所以,在Go语言中很少直接使用数组。
一、定义数组
当在Go语言中使用a := [3]int{1, 2, 3}声明一个数组时,Go底层会在内存中开辟一段固定长度的、连续的空间存放数组中的各个元素,这些元素类型必须相同,可以是基础数据类型,也可以是自定义的Struct结构类型。
需要注意的数组的关键词是:固定长度、连续空间、元素类型相同。
数组结构如下图所示:
可以看到,数组分为3部分:索引(index)、元素类型(type)、元素值(value)。
在Go中,在创建数组时,如果没有显式的指定数组元素,那么Go底层会默认初始化每个元素为对应类型的零值。其中数字类型初始化为0,字符串类型初始化为空字符串””,布尔类型初始化为false,指针类型初始化为nil。
二、操作数组
1、访问数组
Go语言中访问数组的某个元素直接指定下标即可:
若遍历访问数组,则需要使用for…range…
2、截取数组
在Go语言中,没有像PHP中有那么多的数组处理函数,Go语言中截取数组直接用array[low:high:max]的方式,其中max可以不写,【关于具体的截取逻辑,单独再写】。
但是需要注意的是,数组截取之后的数据已经不是数组了,是切片Slice。
看下面的数组截取示例:
这里low=2,high=5,那么可以理解为:从数组d中截取数据,从索引2开始,截取到索引5为止,且不包含索引5的元素,也就是左包右不包。
若是d[:5]则表示从索引0开始截取,截取索引5-1;
若是d[1:]则表示从索引1开始截取,截取到最后。
3、数组元素添加&删除
数组一经定义,不可增删,所以无法对数组进行元素增加。
虽然Go语言没有提供像PHP中的unset()函数来删除数组元素,但能通过截取重拼的方式去实现伪删除,但是此时已经不是数组了,是切片Slice。
三、使用数组
在Go语言中,数组属于值类型。
而在Go语言中,函数参数的传递方式为值传递,所以给函数传参时,每个参数都是原变量的一份拷贝。
那么值传递有以下特点:
- 如果原变量体积庞大,又要复制一份副本,会导致虚耗内存,效率低下
- 函数中若对参数的修改,只在函数内部生效,对原变量无效
由于数组是值类型,也会有值传递的特点。那么怎么去避免这种拷贝且能对原数据进行修改呢?
答案就是传递数组的指针。【数组的指针和指针数组不是一个东西】
1、指针数组与数组的指针
指针数组:
指针数组表示数组内的元素都是指针。
如下图所示,定义一个int指针数组:
如上所示,new(int)表示创建一个int类型的数据组对象,并返回指向这个对象的指针。
如果想给指针数组赋值已有变量的指针地址,则可以这样:
在内存中,指针数据的结构如下所示:
对于指针数组,此时可能会陷入一个误区就是,当把指针数组作为参数传递时,在函数中就能改变其值了呀!
仔细想想,肯定不是的,指针数组的元素是指针,并不是值。在函数中,只能改变指针指向的值,而不能改变指针元素本身,且如果指针数组中存在大批量指针地址数据时,还是有值拷贝的低效率风险。
2、数组的指针
将数组的指针作为参数传递则可以避免大数据体积拷贝的问题,且还能在函数中改变原数组元素的值。
如下为传递数组指针:
需要明白的是,Go语言中函数的传参方式为值传递,即使传递的是个指针地址,也是拷贝一份进行传递。
函数获取到的参数是一个是指针,参数的地址是指针的指针。
来看下面的打印输出:
对应图示就是:
四、数组存储不同类型的元素
Go语言是强类型语言,数组必须指定长度和数据类型,那么Go语言数组能和PHP一样存储不同类型的元素吗?
其实是可以的,Go语言中任何数据类型都能转为interface{}类型,所以通过指定数组元素类型为interface{}就可以实现放进去的元素类型不一致。【其实在Go看来是一致的,都是interface{}类型】
但我们最好还是不要这么做,这样会导致数据不明确,给代码带来不必要的麻烦。
五、最后
本篇文章主要讲解了Go语言数组的基本定义与使用。
Go语言数组是固定类型与长度的,类型是值类型,所以不适合作为参数进行传递,虽然传递数组的指针可以避免值拷贝,但是由于其固定特性,作为参数也是不合适的。
所以在Go语言中,一般使用Slice和Map代替数组进行各种操作。Slice和Map是动态的类似数组的数据类型,使用更加灵活。Slice和Map的底层也是数组结构。
