数组与切片
数组
所谓数组,即内存上一块连续的存储空间。使用方式
// 声明数组
var nums [3]int// 数组赋值
nums[0] = 1
nums[1] = 2
nums[2] = 3// 输出数组元素
for i:=0; i<3; i++ {fmt.Println(nums[i]) //1、2、3
}
我们可以利用数组批量处理数据,看似很方便,但数组在实际的工程项目中是很鸡肋的,因为它无法动态改变长度,也就是无法扩容。
比如我现在想再加一个数字“4”,如果你这样做
nums[3] = 4
这段代码编译的时候会直接报“数组越界”的错误,因为数组的长度是在声明的时候就已经决定好的,声明后就不能再改变数组的长度了。
invalid argument: index 3 out of bounds [0:3]
从内存管理的角度来看,当你声明nums数组的时候,内存会直接分配一段固定的连续空间,也就是长度是无法再修改的了。假如这段空间不固定,我们可以继续在其后面存储数据,那这段代码有可能导致其他应用崩溃,因为它有可能覆盖掉其他应用的内存数据,这是十分危险的行为。
那么我们该怎么把4放入nums中呢?重新声明一个长度为4的数组呗
// 声明一个长度为10的数组
var newNums [10]int// 将nums的元素拷贝进newNums中
for i:=0; i<3; i++ {newNums[i] = nums[i]
}// 把4放入newNums中
newNums[3] = 4// 输出数组元素
for i:=0; i<4; i++ {fmt.Println(newNums[i]) //1、2、3、4
}
上面的代码我们先重新声明了一个长度为4的数组newNums,然后将nums的元素放入到newNums,最后将4放入newNums。这个方法其实相当于我们手动将nums扩容。
虽然我们现在实现了手动扩容数组,但是还有一个问题,如果后续我们要加入5、6、7、8、9...总不能每次都写这么一段代码吧,而且频繁的分配内存也是不可取的。就像你去银行取钱,你预计这个月要支出30万,但单天只需要1万,你总不能一天取一万,这个月每天都跑一趟吧,其时间成本是非常高的。最好的办法其实就是一次多取一些钱,当然,也不一定要一次性全取出,因为30万是预计金额,所以是有够花和不够花两种结果的。那么,我们代码再改一下
// 声明一个长度为4的数组
var newNums [10]int// 将nums的元素拷贝进newNums中
for i:=0; i<3; i++ {newNums[i] = nums[i]
}// 把4放入newNums中
newNums[3] = 4// 输出数组元素
for i:=0; i<4; i++ {fmt.Println(newNums[i]) //1、2、3、4
}newNums[4] = 5
newNums[5] = 6
newNums[6] = 7
newNums[7] = 8
// 输出数组元素
for i:=0; i<8; i++ {fmt.Println(newNums[i]) //1、2、3、4
}
Ok,现在数组使用起来是不是方便了一些?但我们不能每使用一个数组都来这么一套,所以我们需要拿出编程界的扛把子思维——封装。
但这里我就不封装了,别问为什么,因为这非常麻烦,而且我很懒,不想写。那怎么办?我们需要拿出编程界的另一个扛把子思维——不要重复造轮子or站在巨人的肩膀上。是的,这一套已经有人封装好了,而且是go底层支持的,那就是——切片!
切片
我们先看一下切片的结构:
type slice struct {array unsafe.Pointer // 元素指针len int // 长度 cap int // 容量
}
我们可以把切片看作是上面手动扩容数组过程的封装,比如我们使用切片的时候是这样使用的
更新中...