重学 Golang 之数组与切片

数组和切片是 Golang 中比较重要的一部分。
作为一个从PHP转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\n", len)
fmt.Printf("a1容量是:%d\n", 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\n", s2)
s3 := s2[2:5]
fmt.Printf("s3的内容是:%#v\n", 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

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值