7.1 数组
数组是一个定长的序列,大小不能增加。
数组内的元素都是同类型的,类型可以任意。
数组长度也是类型的一部分,所以[5]int和[6]int是以不同的类型。
数组的长度必须在声明的时候就给出,如果不知道,则用[…]int,编译器会自动根据元素判断。
数组长度最大为2G。
数组是值类型。所以传递给函数时是值拷贝。
声明:var arr [5]int
在内存中,数组的元素是整型值,所以每个元素的初始值为0。
package main
import (
"fmt"
)
func main() {
arr := [5]int{1, 2, 3, 4}
var arr2 [5]int
arr2 = [5]int{1, 2, 3}
var arr3 = [2][3]int{ //多维数组
{1, 2, 3},
{4, 5, 6},
}
fmt.Println(arr) //[1 2 3 4 0]
fmt.Println(arr2) //[1 2 3 0 0]
fmt.Println(arr3) //[[1 2 3] [4 5 6]]
}
7.2 切片
切片可以看作是不定长的数组,大小可以改变。
切片可以从数组中得到,可以是整个数组,也可以是数组的子集。
切片是引用类型。
切片是不定长的,所以在声明时不需要指定大小。
可以使用len()函数计算切片长度,使用cap()函数获取切片容量。它等于切片长度+数组除切片之外的长度。
声明:var sli []int
切片其实是对数组的引用,在声明切片的时候,底层是有一个数组的,因为切片是引用类型,没数组它引用谁的呢?对切片的操作,就是对底层的那个数组操作。
所以,如果想要对一个数组进行操作,那么就要声明一个该数组的切片,切片是引用类型,切片的改变会使得数组的改变。
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4} //数组arr
sli := arr[1:] //切片[2,3,4]
alterarr(sli)
fmt.Println(arr) //原数组被更改为[1,3,3,4]
}
func alterarr(a []int) {
a[0] = 3 //把切片的第一个元素2改为3
}
如果想要声明固定的长度和容量的切片,需要make()函数:
arr := make([]int, 5, 10) //声明一个长度为5,容量为10的切片
切片也能切字符串,但是字符串是纯粹不可改变的,所以不能像数组那么利用切片改值。如果要修改字符串,可以先将字符串转为字节数组,修改完再从字节数组转回字符串:
package main
import "fmt"
func main() {
s := "aaaaaaa"
sb := []byte(s) //转为字节数组
sb[0] = 'A' //修改字节,注意这里不能用双引号
ss := string(sb) //转回字符串
fmt.Println(ss) //Aaaaaaa
}
切片之间可以使用copy函数。
切片追加元素可以使用append()函数,甚至可以追加切片。
package main
import "fmt"
func main() {
sli1 := []int{1, 2, 3}
sli2 := make([]int, 10)
_ = copy(sli2, sli1) //复制切片
fmt.Println(sli2) //[1 2 3 0 0 0 0 0 0 0]
sli2 = append(sli2, 9) //追加元素
fmt.Println(sli2) //[1 2 3 0 0 0 0 0 0 0 9]
}
遗憾,Go没有删除切片元素的函数,只能利用切片的性质性质自己改。
切片的排序可以使用sort()函数。
7.3 new()和make()的区别
- 相同:
- 均在堆上分配内存。
- 不同:
- new(T):为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 *T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{}。
- make():返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:切片、map 和 channel。
换言之,new() 函数分配内存,make() 函数初始化。