数组和切片
数组和切片
数组
数组是同一类型元素的集合,例如,整数集合 5,8,9,79,76 形成一个数组。Go 语言中不允许混合不同类型的元素,例如包含字符串和整数的数组。(但如果是 interface{} 类型数组,可以包含任意类型)(数组的默认值与它的数据有关系,数组默认存放类型的空值)
数组的声明
一个数组的表示形式为 [n]T。n 表示数组中元素的数量,T 代表每个元素的类型。元素的数量 n 也是该类型的一部分,数组一但定义,长度就固定了,无法更改
var a [4]int // 声明一个长度为4的整型数组
a := [4]int{1,2,3,4} // 使用简略声明
a := [4]int{12} // 不足用空补全[12 0 0 0]
a := [...]int{1,2,3,4} // 系统自动计算长度
var a [4]int = [4]int{1,2,3,4} // 声明并赋值
a[1] = 10 // 给数组赋值
- 数组的长度也是类型的一部分,不同长度的数组是不同类型
数组是值类型
Go 中的数组是值类型而不是引用类型。这意味着当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。如果对新变量进行更改,则不会影响原始数组。
func main() {
a := [...]int{12,2,3,4}
var b = a
b[1] = 222
fmt.Println(a)
}
使用range迭代数组
数组的长度len(数组)
func main() {
a := [4]int{1,2,3,4}
for _,v := range a { // 不需要的值用_接收
for i,v := range a {
fmt.Println(i,v)
}
}
0 1 // 将索引和对应值返回出来
1 2
2 3
3 4
range是go语言的关键字,不是内置方法,无需加括号
多维数组
本质就是数组嵌套数组
var a [3][3]int = [3][3]int{{1,2,3},{1,2,3},{1,2,3}}
fmt.Println(a)
数组的大小是类型的一部分
切片
切片是由数组建立的一种方便、灵活且功能强大的包装。切片本身不包含任何数据,它只是对底层数组的引用
切片的创建
带有T类型元素的切片由[]T来表示
创建切片需要先创建一个数组
a := [5]int{1,2,3,4,5}
var b []int = a[1:3] // [2,3,4]
fmt.Println(len(b)) // 长度为3
fmt.Println(cap(b)) // 容量为4
切片的修改
切片自己不包含任何数据,它只是底层数据的一种表示
a := [5]int{1,2,3,4,5}
b := a[1:3]
b[1] = 222
fmt.Println(b)
fmt.Println(a)
修改切片b,会导致底层数组a同时发生改变
当多个切片使用同一个数组是,发生的改变会通过底层数组互相影响
切片的内容追加
-
切片的长度和容量
切片的长度是切片中的元素数。切片的容量是从创建切片索引开始的底层数组中元素数。
数组的长度是固定的,它的长度不能增加,但切片是动态的,使用append可以将新元素添加到切片上,append 函数的定义是 func append(s[]T,x ... T)[]T。
x ... T 在函数定义中表示该函数接受参数 x 的个数是可变的。这些类型的函数被称为[可变函数]。
a := [5]int{1,2,3,4,5}
b := a[1:3]
b = append(b,1,2,3,4,5,6)
b[2] = 123456
fmt.Println(b)
fmt.Println(a)
fmt.Println(cap(b)
[2 3 123456 2 3 4 5 6]
8
[1 2 3 4 5]
切片是由数组支持的,并且数组的长度本身是固定的,当切片追加长度超过切片容量时,就要改变底层数组长度,但这是不可以的,所以,当切片长度超过容量是会创建一个长度是原来容量两倍的新数组来作为底层数组
使用make创建切片
func make([]T,len,cap)[]T 通过传递类型,长度和容量来创建切片。容量是可选参数, 默认值为切片长度。make 函数创建一个数组,并返回引用该数组的切片。
a := make([]int,5,6)
var 变量名 切片类型 = make(切片类型,长度,容量)
使用make创建的切片内部初始都是零值
空切片的为nil一个nil切片的长度和容量都为0,可以使用append函数将数据添加到内部
也可以使用 ... 运算符将一个切片添加到另一个切片。
func main() {
veggies := []string{"potatoes", "tomatoes", "brinjal"}
fruits := []string{"oranges", "apples"}
food := append(veggies, fruits...)
fmt.Println("food:",food)
}
切片的函数传递
可以认为切片内部可由一个结构体类型表示
type slice struct {
Length int
Capacity int
ZerothElement *byte
}
切片包含长度、容量和指向数组第零个元素的指针。
所以当切片通过值传递,指针也会指向同一个底层数组,因此当切片作为参数传递给函数时,函数内所做的更改也会在函数外可见。
func subtactOne(numbers []int) {
for i := range numbers {
numbers[i] -= 2
}
}
func main() {
nos := []int{8, 7, 6}
fmt.Println("slice before function call", nos)
subtactOne(nos) // function modifies the slice
fmt.Println("slice after function call", nos) // modifications are visible outside
}
array before function call [8 7 6]
array after function call [6 5 4]
多维切片
类似于数组,切片可以有多个维度。
func main() {
pls := [][]string {
{"C", "C++"},
{"JavaScript"},
{"Go", "Rust"},
}
for _, v1 := range pls {
for _, v2 := range v1 {
fmt.Printf("%s ", v2)
}
fmt.Printf("\n")
}
}
程序的输出为,
C C++
JavaScript
Go Rust
内存优化
切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。在内存管理方面,这是需要注意的。让我们假设我们有一个非常大的数组,我们只想处理它的一小部分。然后,我们由这个数组创建一个切片,并开始处理切片。这里需要重点注意的是,在切片引用时数组仍然存在内存中。
一种解决方法是使用 [copy] 函数 func copy(dst,src[]T)int 来生成一个切片的副本。这样我们可以使用新的切片,原始数组可以被垃圾回收。
package main
import (
"fmt"
)
func countries() []string {
countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
neededCountries := countries[:len(countries)-2]
countriesCpy := make([]string, len(neededCountries))
copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
return countriesCpy
}
func main() {
countriesNeeded := countries()
fmt.Println(countriesNeeded)
}
在上述程序的第 9 行,neededCountries := countries[:len(countries)-2 创建一个去掉尾部 2 个元素的切片 countries,在上述程序的 11 行,将 neededCountries 复制到 countriesCpy 同时在函数的下一行返回 countriesCpy。现在 countries 数组可以被垃圾回收, 因为 neededCountries 不再被引用。

浙公网安备 33010602011771号