// 数组: new([len]Type)
arr := new([5]int)
arr := [5]int{1, 2} // [5]int{1, 2, 0, 0, 0}
arr := [5]{1, 2, 3, 4, 5}
arr := [...]{1, 2, 3, 4, 5} // [5]int{1, 2, 3, 4, 5}
arrKV := [...]int{1: 10, 6: 20, 30} // [8]int{0, 10, 0, 0, 0, 0, 20, 30}
// 切片: make([]Type, size[, cap])
slice := make([]int, 0) // []int{}
slice := make([]int, 5) // []int{0, 0, 0, 0, 0}
slice := make([]int, 5, 10) // []int{0, 0, 0, 0, 0}
// 在预先知道所需切片大小时可以预先分配好底层数组, 避免 append 时频繁扩容
slice := make([]int, 0, 100) // []int{}
// 从现有数组/切片中截取: arr[start:end:max]
// 其中: start <= end <= max <= len(arr)
arr := [20]int{}
slice := arr[1:5:10]
# 1. 数组
# 1.1 概念
[5]int[10]int001声明的格式是:
var identifier [len]type
例如:
var arr1 [5]int
new()var arr1 = new([5]int)var arr2 [5]intarr1*[5]intarr2[5]int# 1.2 数组常量
[]=var arrAge = [5]int{18, 20, 15, 22, 16}
var arrLazy = [...]int{5, 6, 7, 8, 22}
var arrLazy1 = []int{5, 6, 7, 8, 22} //注:初始化得到的实际上是切片slice
var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}
var arrKeyValue1 = []string{3: "Chris", 4: "Ron"} //注:初始化得到的实际上是切片slice
第一种变化:
var arrAge = [5]int{18, 20, 15, 22, 16}
[5]int[10]int {1, 2, 3}0数组长度写成 ...var arrLazy = [...]int{5, 6, 7, 8, 22}
...key: value 语法var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}
只有索引 3 和 4 被赋予实际的值,其他元素都被设置为空的字符串,所以输出结果为:
Person at 0 is
Person at 1 is
Person at 2 is
Person at 3 is Chris
Person at 4 is Ron
...// [ 0:"" 1:"" 2:"" 3:"Chris" 4:"Ron" 5:"Tom" 6:"Dav" ]
var arr = [...]string{3: "Chris", 4: "Ron", "Tom", "Dav"}
// [ 0:"" 1:"" 2:"" 3:"Chris" 4:"Ron" 5:"Tom" 6:"Dav" 7:"" 8:"" 9:"" ]
var arr = [10]string{3: "Chris", 4: "Ron", "Tom", "Dav"}
// [ 0:"" 1:"" 2:"" 3:"Chris" 4:"Ron" 5:"" 6:"" 7:"" 8:"Tom" 9:"Dav" ]
var arr = [10]string{3: "Chris", "Ron", 8: "Tom", "Dav"}
# 2. 切片
# 2.1 概念
切片 (slice) 是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。
len()给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是,切片的长度可以在运行时修改,最小为 0, 最大为相关数组的长度:切片是一个 长度可变的数组。
cap()scap(s)s[0]s0 <= len(s) <= cap(s)多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。
优点:因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中切片比数组更常用。
注意:绝对不要用指针指向切片。切片本身已经是一个引用类型,所以它本身就是一个指针!!
声明切片的格式是:
var identifier []type // 不需要说明长度
nil切片的初始化格式是:
// start <= end <= max <= len(arr1)
var slice1 []type = arr1[start:end:max]
slice1arr1startend-1start:end:maxslice1[0]arr1[start]arr1var slice1 []type = arr1[:]slice1arr1arr1[0:len(arr1)]slice1 = &arr1// 示例
var arr = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := arr[:] // [0 1 2 3 4 5 6 7 8 9]
s2 := arr[1:5] // [1 2 3 4]
s3 := arr[1:] // [1 2 3 4 5 6 7 8 9]
s4 := arr[5:] // [5 6 7 8 9]
s5 := arr[1:5:8] // [1 2 3 4]
maxslice1arr1max-1max-1slice1arr1startmax-1var arr = [...]int{0, 1, 2, 3, 4, 5, 6}
var s = arr[2:4:5] // [2 3]
fmt.Println(s, arr) // [2 3] [0 1 2 3 4 5 6]
s = append(s, 100) // 此时还共享 arr 地址
fmt.Println(s, arr) // [2 3 100] [0 1 2 3 100 5 6]
s = append(s, 200) // 此时已新开辟新的数组, 不再和 arr 共享地址
fmt.Println(s, arr) // [2 3 100 200] [0 1 2 3 100 5 6]
s[1] = 300 // 不再和 arr 共享地址
fmt.Println(s, arr) // [2 300 100 200] [0 1 2 3 100 5 6]
# 2.2 用 make() 创建一个切片
make()var slice1 []type = make([]type, len)slice1 := make([]type, len)lenslices2 := make([]int, 10)cap(s2) == len(s2) == 10make()slice1lenslice1 := make([]type, len, cap)make()func make([]T, len, cap)cap所以下面两种方法可以生成相同的切片:
make([]int, 50, 100)
new([100]int)[0:50]
# 2.3 new() 和 make() 的区别
看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。
new(T)T0*TT0&T{}make(T)切片mapchannelnew()make()new() 是一个函数,不要忘记它的括号。