数组
数组是同一类型的元素集合;
声明
func main() {
var a [3]int // 声明一个长度为3的整数类型的数组,元素为零值
b := [3]int{4,5,6} // 简略声明
c := [3]int{4} // 简略声明, 未赋值的为零值
fmt.Println(a) // [0 0 0]
a[0] = 1
a[1] = 2
a[2] = 3
fmt.Println(a) // [1 2 3]
fmt.Println(b) // [4 5 6]
fmt.Println(c) // [4 0 0]
}
func main() {
//var a [...]int // 运行报错
b := [...]int{1,2,3,4} // `...`可以根据赋值计算出数组的长度
c := [...]int{1,2}
fmt.Println(b)
fmt.Println(c)
}
// go中的数组是值传递,当把数组中的元素赋值给一个变量时,该变量获取的时一个元素的副本,变更时不会影响数组元素。函数中也是。
func main() {
//var a [...]int
b := [...]int{1,2,3,4}
var i int = b[0]
fmt.Println(b)
fmt.Println(i)
i=i+10
fmt.Println(b)
fmt.Println(i)
/* 输出:
[1 2 3 4]
1
[1 2 3 4]
11
*/
}
1、go中函数传递都是值传递
2、slice、map、channel都是引用类型,即便是值传递,结构内部还是指向原来的引用对象,所以函数体内可以直接修改元素。
3、如果slice触发扩容,data会指向新的底层数组,而不指向外部的底层数组了。所以之后再修改slice,不会对外部的slice造成影响。值类型(值传递) :传递值本身,即把相同的数据复制一份进行传递,传递前有一份传递后就有了两份相同的数据,两份数据相互隔离,任意数据的修改只会对本数据产生影响。
引用类型(引用传递):传递同一个数据,即传递后两个数据所指向的均为同一个数据,明显的就是两个指针变量,指向同一个数据,任一变量的修改都会对数据造成影响。
// 遍历数组
func main() {
b := [...]int{1,2,3,4}
// 常用for循环
for i := 0;i<len(b);i++ {
fmt.Println("b[i] = ",b[i])
}
// range 循环
for i,v := range b{
//for _,v := range b{ // 忽视下标
fmt.Println("第 ",i," 是 ",v)
}
}
二维数组
func main() {
a := [3][2]string{// 创建并赋值
{"lisi","18"},
{"wangwu","19"},
{"oneyi","20"}, //注意要加`,`
}
fmt.Println(a)
var b [2][2]string // 创建后初始化
b[0][0] = "yi"
b[0][1] = "1"
b[1][0] = "er"
b[1][1] = "2"
fmt.Println(b)
}
// 输出:
// [[lisi 18] [wangwu 19] [oneyi 20]]
// [[yi 1] [er 2]]
切片
切片是对数组的引用,本身不拥有任何数据,是一种方便,灵活的包装;
func main() {
a := [5]int{100,110,120,130,140}
var slice_b []int = a[1:3] // 创建一个从 a 数组索引 start 开始到 end - 1 结束的切片
b := []int{6,7,8} // 声明1:创建一个切片
fmt.Println(slice_b) // 输出: [110 120]
fmt.Println(b) // 输出: [110 120]
//声明2:make([]type,len,[cap]) 1(是type而不是[]type):切片元素的类型,2:切片的长度,3:切片的容量
// make() 通过传递类型,长度和容量来创建切片。容量是可选参数, 默认值为切片长度。make 函数创建一个数组,并返回引用该数组的切片。
i := make([]int,5,5)
fmt.Println(b)
// 声明3:指定数组创建切片 使用具体的元素创建且品牌
var sliceName []string = []string{"HaiCoder", "haicoder"}
}
func main() {
a := [5]int{100,110,120,130,140}
fmt.Println(a)
b := a[:] // 所有数据 100 ~ 140
fmt.Println(b)
for i,v :=range a{
//v 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,
//对它所做的任何修改都不会影响到集合中原有的值
//v++ // 所以 v++ 不会修改任何值
b[i]++ // 因为切片不拥有任何数据,只是对数组的引用(引用类型),即对切片的任何修改都会再数组中展示出来
fmt.Println(i," = ",v)
}
fmt.Println(b)
fmt.Println(a)
}
// [100 110 120 130 140]
// [100 110 120 130 140]
// 0 = 100
// 1 = 110
// 2 = 120
// 3 = 130
// 4 = 140
// [101 111 121 131 141]
// [101 111 121 131 141]
切片的长度是切片的元素数量,切片的容量是从创建切片索引开始,数组的元素数量
func main() {
a := [5]int{100,110,120,130,140}
fmt.Println("a len() = ",len(a)," cap() = ",cap(a))
c := a[2:4] //创建一个切片 c
fmt.Println("c len() = ",len(c)," cap() = ",cap(c))
// a len() = 5 cap() = 5
// c len() = 2 cap() = 3 // 即下标2开始的数组元素数量:3个(120,130,140)
c = c[:cap(c)] // 重置切片容量; c[0:3]
fmt.Println("c len() = ",len(c)," cap() = ",cap(c),"\n",c)
// c len() = 3 cap() = 3
// [120 130 140]
}
func main() {
// make() 通过传递类型,长度和容量来创建切片。容量是可选参数, 默认值为切片长度。make 函数创建一个数组,并返回引用该数组的切片。
i := make([]int,5,5)
fmt.Println(i) //输出默认值: [0 0 0 0 0]
// 创建两个数组
var a [5]string
b := [5]string{
"yi",
"er",
"san",
"si",
"wu",
}
fmt.Println(a,b) // [yi er san si wu]
// slice 追加元素,数组的长度是固定的,切片的长度是动态的,使用函数 append() 可以将新的元素(可以是多个)添加到切片中
c :=append(b[:],"liu")
fmt.Println(c) // [yi er san si wu liu]
c =append(c,"qi","ba")
fmt.Println(c) // [yi er san si wu liu qi ba]
// slice 类型的默认值是 `nil` ,一个为nil切片的长度和容量为0,可以使用append()添加
var d []int
if d == nil {
d = append(d,1,2,3,4,5,6)
}
fmt.Println(d) // [1 2 3 4 5 6]
}
// 结构体来表示切片
type slice struct {
Length int // 长度
Capacity int // 容量
ZerothElement *byte //指向数组第零个元素的指针
}
func main() {
// 当slice传递给函数(go中函数传递都是值传递)是值传递, 因为slice没有实际的数据 只是数组数据的引用,
//因此当一个函数修改一个slice时,会对其指向的数组做显性的修改(即改变数组的数据)
}
内存优化,因为切片只是对数组的引用所以当切片再引用时数组仍然存在内存中的。
// 使用copy()函数可以来生产一个切片的副本,这样原数组就可以被回收掉了
func main() {
b := [5]string{"yi","er","san","si","wu"}
//var c []string
c := make([]string,3)
copy(c,b[0:3])
fmt.Println(c) //[yi er san]
}