var a [len]intvar a [5]intvar a[5] intvar a[10]intlen-1
for i := 0; i < len(a); i++ {}
for index, v := range a {}
panic[n]*T*[n]T
数组定义
var 数组变量名 [元素数量]Type
var 数组变量名 = [元素数量]Type{元素……}
var 数组变量名 = [……]Type{元素……}
数组变量名 := [……]Type{元素……}
数组变量名 := [元素数量]Type{元素……}
数组遍历
方法1
a := [...]int{1:1, 3:5}
for i := 0; i < len(a); i++ {
fmt.Print(a[i], " ")
}
方法2
a := [...]int{1:1, 3:5}
for _, value := range a {
fmt.Print(value, " ")
}
多维数组
// 多维数组
全局:
var 数组名 [行][列]类型
var 数组名 [...][列]类型
局部:
数组名 := [行][列]类型{{第一行},{第二行},...,{第n行}}
数组名 := [...][列]类型{{第一行},{第二行},...,{第n行}}
// 多维数组遍历
func main() {
var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
for k2, v2 := range v1 {
fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
}
fmt.Println()
}
}
数组的内存管理
数组,定长且元素类型一致的数据集合
(1)数组内存是连续的
(2)数组的内存地址实际上就是数组第一个元素的内存地址
数组中的元素是可变的,但长度是固定的,不可更改的
拷贝,,复制+粘贴,,另创一个空间存储拷贝的数组
切片切片Slice并不是数组或者数组指针,它通过内部指针和相关属性引用数组片段,以实现变长方案
//切片的数据结构定义
type slice struct {
array unsafe.Pointer
len int
cap int
}
//切片的结构体由3部分构成,Pointer是指向一个数组的指针,len代表当前切片的长度,cap是当前切片的容量
len()cap
切片的定义
var 切片名 [] 类型名
// 切片不需要说明长度
// 使用make()函数来创建切片:
var slice [] type = make([]type,len)
简写为
slice := make([]type,len)
// 指定容量,其中capacity为可选参数
slice := make([]T,length,capacity)
// len是数组的长度,并且也是切片的初始长度
切片创建及初始化
// 直接初始化切片,[]表示是切片类型,{1,2,3}初始化依次是1,2,3,其中cap=len=3
s := []int {1,2,3}
// 初始化切片s,是数组arr的引用
s := arr[:]
// 将数组arr从下标startIndex到endIndex-1的元素创建一个新的切片
s := arr[startIndex:endIndex]
// 默认endIndex时将表示一直到arr的最后一个元素
s := arr[startIndex:]
// 默认startIndex时将表示从arr的第一个元素开始
s := arr[:endIndex]
// 通过切片s初始化切片s1
s1 = s[startIndex:endIndex]
// 通过内置函数make()初始化切片s,[]int标识为其元素类型为int的切片
// make()函数允许在运行期动态指定数组长度,绕开了数组类型必须使用编译期常量的限制
s := make([]int,len,cap)
var s = make([]int,len,cap)
// make函数只用于切片,字典,channel创建
切片的指针类型
var v1 = new([]int) //创建并初始化,指向默认0
var v2 *[]int //无初始化,指向nil
nil和空切片
nillen(s) == 0s == nil
len()函数、cap()函数
len()cap()nilnilnil
var s1 []int //len(s1) = 0;cap(s1) = 0; s1 == nil
s2 := []int{} //len(s2) = 0;cap(s2) = 0; s2 != nil
s3 := make([]int,0) //len(s3) = 0;cap(s3) = 0; s1 != nil
append()函数、copy()函数
- append()函数,增加切片元素
- copy()函数,拷贝
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
Slice底层的实现
-
切片的设计想法是由动态数组概念而来的,为了方便的使一个数据结构自动增加或减少,但是它本身并不是动态数据或者数组指针
-
在Go中,数组是值类型,赋值和函数传参操作都会复制整个数组数据
-
因此当数组元素量多的话,会消耗大量的内存,于是,函数传参用数组的指针,也就是说开辟一个空间存放数组,然后设置一个指针指向数组的开端即可,以高效利用内存
-
但是,传指针有一个弊端,就是当原数组的指针指向更改了,那么函数里的指针指向也会更改
-
而用切片传数组参数,既可以达到节约内存的目的,也可以达到合理处理好共享内存的问题
-
故,将一个大数组传递给函数会消耗很多内存,而采用切片的方式传递参数可以避免上述问题
-
切片是引用传递,所以不需要使用额外的内存并且比使用数组更有效率
-
切片本身并不是动态数组或者数组指针,其内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一个只读对象,其工作机制类似数组指针的一种封装
-
切片是对数组一个连续片段的引用,是一个引用类型,,类似python中的list类型
slice := []int{10,20,30,40,50,60}
// 创建一个len=6,cap=6的切片,同时完成数组里面每个元素值的初始化
// 需要注意的是,[]里面不能写数组的容量,因为如果写了数以后就是数组了,而不再是切片了
切片的底层原理
切片的本质是就是一个框,框住了一个连续的内存,只能保存相同类型的元素。
属于引用类型,真正的数据都是保存在底层数组中的
Slice的自动扩容策略
-
如果新申请的容量比原来容量的2倍大,则新申请的容量就是最终容量;
-
否则
int