// 数组: 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]int
001

声明的格式是:

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]arr1
var 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-1
var 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)lenslice
s2 := make([]int, 10)cap(s2) == len(s2) == 10
make()
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)切片mapchannel
new()make()

new() 是一个函数,不要忘记它的括号