文章目录
1 定义
数组存储一系列相同类型的值,定义数组的语法是:
关键字 变量名 数组长度 数组元素类型
var a [3]int
定义一个数组,初值为[0,0,0,…]
package main
import "fmt"
func main() {
var a [3]int
fmt.Println(a) // [0 0 0]
}
获取数组元素并给数组元素赋值
-
设置元素语法
package main import "fmt" func main() { var a [3]int a[0] = 12 // 给第一个元素赋值 a[1] = 13 // 给第二个元素赋值 a[2] = 14 // 给第三个元素赋值 fmt.Println(a) // [12 13 14] }
-
读取元素语法
package main import "fmt" func main() { var a [3]int a[0] = 12 a[1] = 13 a[2] = 14 fmt.Println(a) // [12 13 14] for i := 0; i < len(a); i++ { fmt.Printf(" %d", a[i]) // 12 13 14 } }
2 通过简略声明创建数组
语法
变量名 := [元素个数]元素类型{元素1,元素2,元素3}
a := [3]int{12,13,14}
测试
package main
import "fmt"
func main() {
a := [5]int{1, 2, 3, 4, 5}
for i := 0; i < len(a); i++ {
fmt.Printf(" %d", a[i]) // 1 2 3 4 5
}
}
3 通过简略声明创建数组
创建数组的时候可以用…代替,编译器会帮助计算数组的元素个数。
语法
// 变量名 := [...]元素类型{元素1,元素2,元素3,...}
a := [...]int{1,2,3,4,5,6}
测试
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(a); i++ {
fmt.Printf(" %d", a[i]) // 1 2 3 4 5 6
}
}
4 可以给数组赋一个值
对于一个有三个元素的数组,可以给数组赋一个值,没必要三个值都赋。对于其他没有赋值的元素,Go会默认将其赋值为零值。
package main
import "fmt"
func main() {
a := [3]int{12}
fmt.Println(a) // [12 0 0]
}
5 数组的长度是数组类型的一部分
数组的长度是类型的一部分,所有[3]int 和 [5]int不是相同类型。如果把一个长度为3的整数数组赋值给长度为5的整数数组,会直接报错的。
测试数组长度也是数组的一部分
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
var b [5]int
b = a // cannot use a (type [3]int) as type [5]int in assignment
fmt.Println(b)
}
6 数组是值类型不是引用类型
数组是一个值类型,意味着当把一个数组变量赋值给另一个变量时,是拷贝了一份副本,而不是拷贝了地址(引用类型是通过地址访问),所以当对原数组进行修改的时候,是不会影响到其他副本的,因为在内存中是有两个不同的区域分别存储,所以互不影响。
package main
import "fmt"
func main() {
nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
newnbastars := nbastars // 复制一个副本给newnbastars
nbastars[0] = "jordan" // 对原数组修改,不会影响到副本
fmt.Println(nbastars)
fmt.Println(newnbastars)
}
/*
[jordan james wade paul curry]
[kobe james wade paul curry]
*/
数组做为函数参数,也是传值
package main
import "fmt"
func changeArr(a [5]string) {
a[0] = "new value"
fmt.Println("in changeArr:", a)
}
func main() {
nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
fmt.Println("调用函数前:", nbastars)
changeArr(nbastars)
fmt.Println("调用函数后:", nbastars)
}
// 数组通过值传递,所以不会改变原数组的数据。
//调用函数前: [kobe james wade paul curry]
//in changeArr: [new value james wade paul curry]
//调用函数后: [kobe james wade paul curry]
7 数组长度
通过len(arr) 返回arr数组的长度,len函数通常与数组遍历配合使用,数组的索引范围为:0~len(arr)-1。
通过for+len循环遍历数组
package main
import "fmt"
func main() {
nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
for i := 0; i < len(nbastars); i++ {
fmt.Printf("【%d】---【%s】\n", i, nbastars[i])
}
fmt.Println()
}
返回值
【0】---【kobe】
【1】---【james】
【2】---【wade】
【3】---【paul】
【4】---【curry】
8 通过range对数组进行遍历
package main
import "fmt"
func main() {
nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
for index, value := range nbastars {
fmt.Printf("【%d】---【%s】\n", index, value)
}
}
可以获取与for相同的输出。
如果只想获取元素,对索引不敏感,可以使用“_”下划线替换掉
package main
import "fmt"
func main() {
nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
for _, value := range nbastars {
fmt.Printf("【any】---【%s】\n", value)
}
}
输出结果
【any】---【kobe】
【any】---【james】
【any】---【wade】
【any】---【paul】
【any】---【curry】
9 多维数组
Go支持多维数组,多维数组同样具有两种创建和初始化的方式,第一种是简单声明,第二种是先声明,然后通过索引逐个初始化,同样的,二维数组同样需要对数组的长度做限制。
通过正常方式创建二维数组
package main
import "fmt"
func printArr(a [4][13]int) {
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%d ", v2)
}
fmt.Println()
}
}
func main() {
var poke [4][13]int
poke[0][0] = 01
poke[0][1] = 02
poke[1][0] = 14
poke[2][0] = 27
poke[3][0] = 40
printArr(poke)
fmt.Println(poke)
}
简明声明
package main
import "fmt"
func printArr(a [2][3]string) {
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s ", v2)
}
fmt.Println()
}
}
func main() {
a := [...][3]string{ // 第一个维度可以用...表示,第二个维度就不行了
{"物理", "化学", "生物"},
{"历史", "政治", "地理"},
}
printArr(a)
fmt.Println(a)
}
这里面的第一个维度可以不写长度,第二个维度必须写长度,其实这个和C语言的二维数组一样,第一个维度是可以不需要写长度的,第二个维度必须写长度,因为第二个维度是决定二维数组的内存跨度的。
10 切片的定义
切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper)。切片本身不拥有任何数据。它们只是对现有数组的引用。
10.1 创建一个切片
[]T表示T类型的切片
从数组创建一个切片
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3, 4, 5}
var b []int = a[1:3] // create slice from array
fmt.Printf("type of b=%T, value of b=%s", b, b) // type of b=[]int, value of b=[%!s(int=2) %!s(int=3)]
}
arr[start: end] 获取arr数组的start索引到end-1索引。
{1,2,3,4,5} [1:3] 所以获取到的索引是 1 :2 返回的值是[2, 3]
10.2 使用简单方法创建一个切片
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5} // 创建一个数组,并且返回他的引用,这个引用就是slice类型
fmt.Println(a) // [1,2,3,4,5]
}
11 修改切片
切片本身不拥有数据,所有的数据都存储在底层的数组中,切片是数组的一种表示,修改切片,会影响底层数组中的数据的变化。
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5} // 创建一个数组,并且返回他的引用,这个引用就是slice类型
b := a
fmt.Println("修改b前:", a)
for i, _ := range (b) {
b[i] += 2
}
fmt.Println("修改b后:", a)
}
输出结果
修改b前: [1 2 3 4 5]
修改b后: [3 4 5 6 7]
显然与数组的结果是不一样的,数组是值类型,切片是引用类型。所有切片的副本对切片的操作最终都将反馈到底层数组中。
12 将切片做为函数的参数
切片做为函数的参数,传递到函数内部的其实也是数组的引用,如果在函数内部修改切片的值,同样会直接影响底层数组中的值。
package main
import "fmt"
func changeSlice(a []int) {
for i, _ := range a {
a[i] += 1
}
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println("调用函数之前:", a)
changeSlice(a)
fmt.Println("调用函数之后:", a)
}
函数返回结果
调用函数之前: [1 2 3 4 5 6 7 8]
调用函数之后: [2 3 4 5 6 7 8 9]
从运行结果也能看出来,切片是传递的引用,对切片的修改会直接影响底层数组。
13 切片的长度和容量
切片的长度指的是切片中包含元素的个数
切片的容量指的是切片所引用的底层数组的长度
容量反应了切片最多的元素能有多少个
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
var b []int
b = a[0:5]
fmt.Printf("slice b len:%d, slice b capatity:%d", len(b), cap(b))
// slice b len:5, slice b capatity:8
}
在上面的实例中可以发现,切片的长度是5个,容量就是底层数组的长度,len(a) = 8
14 使用make创建切片
func make([]T,len,cap)[]T 通过传递类型,长度和容量来创建切片。容量是可选参数, 默认值为切片长度。make 函数创建一个数组,并返回引用该数组的切片
package main
import "fmt"
func main() {
i := make([]int, 5, 8)
fmt.Println(i) //[0 0 0 0 0]
// 重置切片
i = i[:cap(i)]
fmt.Println(i) //[0 0 0 0 0 0 0 0]
}
15 切片追加元素
有一个问题可能会困扰你。如果切片由数组支持,并且数组本身的长度是固定的,那么切片如何具有动态长度。以及内部发生了什么,当新的元素被添加到切片时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用。现在新切片的容量是旧切片的两倍
package main
import "fmt"
func main() {
players := []string{"kobe", "james", "jordan"}
fmt.Printf("size of players:%d,cap of players:%d\n", len(players), cap(players))
players = append(players, "paul")
fmt.Printf("size of players:%d,cap of players:%d\n", len(players), cap(players))
players = append(players, "yao", "curry", "topson")
fmt.Printf("size of players:%d,cap of players:%d\n", len(players), cap(players))
}
返回结果
size of players:3,cap of players:3
size of players:4,cap of players:6
size of players:7,cap of players:12
从返回结果看,和理论是相符的。
16 多维切片
多维切片与多维数组类似,切片也可以有多个维度。
17 内存优化
如果一个切片引用一个数组,当程序对这个切片进行操作的时候,底层的数组将不会被垃圾回收机制回收,如果有一个比较大的数组,切片只活跃在很小的范围内,这就导致了大量的内存浪费。我们通过一个实例来看怎么做内存优化。
func copy(dst,src[]T)int 来生成一个切片的副本。这样我们可以使用新的切片,原始数组可以被垃圾回收。
package main
import "fmt"
func main() {
citys := [...]string{"北京", "上海", "重庆", "成都", "广州", "深圳", "东京", "南京"}
cityslice := citys[0:3]
var cityslicenew []string
r := copy(cityslicenew, cityslice[0:cap(cityslice)-2])
fmt.Println(r)
}
cityslicenew 是cityslice 的一个副本,这样copy之后citys就会被垃圾回收了。