数组和切片是 Golang 中比较重要的一部分。 数组是啥就不说了,既然熟悉 PHP 肯定知道数组是啥,剩下的就是切片是啥了。 在 PHP 下,我们熟知数组几乎是万能的,啥都能塞,而且可以轻松的追加和删除,那么 Golang 下的数组呢? 从接触这个 Golang 开始就知道她是一个强类型的语言。 那么也就代表着,数组也只能是同一种类型的集合。 数组的定义如下:

PHP下我们这样创建数组,PHP是一个弱类型语言,所以PHP下的数组也是不限定长度的。可以所以的增删数组,但是在Go下的数组却是另一回事。

数组

初始化数组

Go 中我们这样来创建一个固定长度的数组,Go 语言下的数组是固定长度的,初始化完即固定死了数组长度。

a1 := [5]string{"A","B","C","D","E"}

这里我们初始化了一个长度为5的字符串数组

获取数组长度、容量

既然是数组,那么我们就肯定需要知道数组的长度和容量。

在Go中,可以使用 len() 函数来获取数组长度,使用 cap() 获取数组容量

//code
a1 := [5]string{"A","B","C","D","E"}
len := len(a1)
cap := cap(a1)
fmt.Printf("a1的长度是:%d
", len)
fmt.Printf("a1容量是:%d
", cap)

//output
a1的长度是:5
a1容量是:5

切片

讲完了数组,那么我们再来讲一下切片,以及数组和切片的区别是什么? 在前面我们讲了数组,但是数组的长度是固定的,那么我们需要创建可变的数组怎么办呢? 所以切片就来了,切片和数组最直接的概念就是:长度不可变的叫数组,长度可变的就是切片

创建切片

可以使用 make 函数来创建一个空的切片: make(数据类型,长度,容量)

a1 = make([]string, 5)    //创建长度为5的切片
a2 = make([]string, 6, 7) //创建一个长度为6,容量为7的切片

那么这个切片的长度和容量分别是多少呢?结果是 a1 容量是5,a2 的容量是7

这个是为什么呢?

因为 make 函数中如果不指明其容量,那么它的容量就会和长度一致

创建一个非空的切换以及切片取值,在切片中取值使用的方式跟 PHP 不一样,PHP是直接使用下标取值,Go 语言切片的取值方式是使用 区间表达式 。对,我们没理解错,就是数学中的那个区间取值。

既然是区间表达式,那么就代表着我们看到的只是切片中的一段区间,并不是完整的切片。

//code
s1 := []string{"a","b","c","d","e","f","i"}
s2 := s1[1:3]
fmt.Printf("s2的内容是:%#v
", s2)
s3 := s2[2:5]
fmt.Printf("s3的内容是:%#v
", s3)

我们来想想这段代码输出的内容是什么呢? 如果套用 PHP 下的 array_slice 来理解的:

  • s2 从下部1开始,取3个,那么这个 s2 的值将是: []string{"b","c","d"}
  • s3 从 s2 取值出来的,从下标2开始,那么就只有一个元素了: []string{"d"}

但是真的是这样吗?NO...

Go切片的区间我们就无法正常套用 PHP 的 array_slice 是方式来直接理解了,我们先看看运行结果是什么。

//output
s2的内容是:[]string{"b", "c"}
s3的内容是:[]string{"d", "e", "f"} //为什么s2只有2个元素,为什么s3这个才能取到值呢?

最终发现我们的理解都是错的。

正确的应该这样理解:区间表达式 s1[1:3] 的意思:从下标 1 的开始到下标 3 ,所以命中的区间就是 2和3了,于是结果就是: []string{"b", "c"}

然后再来看看 s3 这个情况,从输出的结果来看,s2 只有2个元素,为什么能取出3个元素呢?

这是因为,在底层数组不变的情况下,切片代表的窗口可以向右扩展,直至其底层数组的末尾。

所以 s2 中的数据元素是:s1 := []string{"b","c","d","e","f","i"},顾 s3 中能输出:[]string{"d", "e", "f"} 也就没问题了。

那么切片既然能从代表窗口像右扩展到尾部,那么能不能从左边扩展呢? 这个是不行的。切片代表的窗口是无法向左扩展的。也就是说,永远无法透过s2看到s1中最左边的那个元素。

最后来说一下怎么获取切片的全部元素,可以使用 cap(s1) 获得切片的容量

s1 := []string{"a","b","c","d","e","f","i"}
s4 := s1[0:cap(s1)]

切片是如何扩容的?

在 PHP 的数组总,是有长度,但是没有容量的概念。

但是在 Go 中,我们知道了切片是有长度和容量的。那么就好奇了,切片是如何扩容的?去搜索了官方的文档,里面有提到了, Go 的切片扩容方式:

  1. 如果一次性追加的内容过多,要追加的切片长度大于原有切片长度的2倍,则扩容的长度直接是新的长度为基准。
  2. 如果一次追加的内容长度不超过原有切片的2倍长度,则再去检查当前切片长度是否 < 1024 ,如果是则按原有容量的2倍来扩容
  3. 如果一次追加的内容长度不超过原有切片的2倍长度,且如果长度 >= 1024,则按原有长度的1.5倍扩容

感兴趣的可以看看 Go 的切片扩容的源码 runtime/slice.go#L163 切片扩容