数组等数据结构
数组
是切片和映射的基础
声明数组
package main
import "fmt"
func main() {
var a [3]int
a[1] = 10
fmt.Println(a[0])
fmt.Println(a[1])
fmt.Println(a[len(a)-1])
}
输出:
0
10
0
初始化数组
package main
import "fmt"
func main() {
cities := [5]string{"New York", "Paris", "Berlin", "Madrid"}
fmt.Println("Cities:", cities)
}
数组中的省略号
如果你不知道你将需要多少个位置,但知道数据元素集,那么还有一种声明和初始化数组的方法是使用省略号 (...),如下例所示:
q := [...]int{1, 2, 3}
package main
import "fmt"
func main() {
cities := [...]string{"New York", "Paris", "Berlin", "Madrid"}
fmt.Println("Cities:", cities)
}
另一种有趣的数组初始化方法是使用省略号并仅为最后一个位置指定值。 例如,使用以下代码:
package main
import "fmt"
func main() {
numbers := [...]int{99: -1}
fmt.Println("First Position:", numbers[0])
fmt.Println("Last Position:", numbers[99])
fmt.Println("Length:", len(numbers))
}
输出:
First Position: 0
Last Position: -1
Length: 100
切片
声明和初始化切片
要声明切片,可采用与声明数组相同的方式操作
目前,切片与数组的区别不大。 可用相同的方式声明这两者。 若要从切片中获取信息,可使用内置函数 len() 和 cap()。
切片项
Go 支持切片运算符 s[i:p],其中:
- s 表示数组。
- i 表示指向要添加到新切片的基础数组(或另一个切片)的第一个元素的指针。 变量 i 对应于数组 array[i] 中索引位置 i 处的元素。 请记住,此元素不一定是基础数组的第一个元素 array[0]。
- p 表示创建新切片时要使用的基础数组中的元素数目,也表示元素位置。 变量 p 对应于可用于新切片的基础数组中的最后一个元素。 可在位置 array[i+1] 找到基础数组中位置 p 处的元素。 请注意,此元素不一定是基础数组的最后一个元素 array[len(array)-1]。
若要用代码表示在上图中看到的内容,可使用以下代码:
package main
import "fmt"
func main() {
months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
quarter1 := months[0:3]
quarter2 := months[3:6]
quarter3 := months[6:9]
quarter4 := months[9:12]
fmt.Println(quarter1, len(quarter1), cap(quarter1))
fmt.Println(quarter2, len(quarter2), cap(quarter2))
fmt.Println(quarter3, len(quarter3), cap(quarter3))
fmt.Println(quarter4, len(quarter4), cap(quarter4))
}
输出:
[January February March] 3 12
[April May June] 3 9
[July August September] 3 6
[October November December] 3 3
请注意,切片的长度不变,但容量不同。 我们来了解 quarter2 切片。 声明此切片时,你指出希望切片从位置编号 3 开始,最后一个元素位于位置编号 6。 切片长度为 3 个元素,但容量为 9,原因是基础数组有更多元素或位置可供使用,但对切片而言不可见。 例如,如果你尝试打印类似 fmt.Println(quarter2[3]) 的内容,会出现以下错误:panic: runtime error: index out of range [3] with length 3。
切片容量仅指出切片可扩展的程度。 因此,你可从 quarter2 创建扩展切片,如下例所示:
package main
import "fmt"
func main() {
months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
quarter2 := months[3:6]
quarter2Extended := quarter2[:4]
fmt.Println(quarter2, len(quarter2), cap(quarter2))
fmt.Println(quarter2Extended, len(quarter2Extended), cap(quarter2Extended))
}
输出:
[April May June] 3 9
[April May June July] 4 9
在声明 quarter2Extended 变量时,无需指定初始位置 ([:4])。 执行此操作时,Go 会假定你想要切片的第一个位置。 你可对最后一个位置 ([1:]) 执行相同的操作。 Go 将假定你要引用所有元素,直到切片的最后位置 (len()-1)。
追加项
我们了解了切片的工作原理,还学习了它们与数组的相似性。 现在,让我们来了解它们与数组之间有何不同。 第一个区别是切片的大小不是固定的,而是动态的。 创建切片后,可向其添加更多元素,这样切片就会扩展。 稍后你将了解基础数组发生的情况。
Go 提供了内置函数 append(slice, element),便于你向切片添加元素。 将要修改的切片和要追加的元素作为值发送给该函数。 然后,append 函数会返回一个新的切片,将其存储在变量中。 对于要更改的切片,变量可能相同。
让我们看一下追加进程在代码中的显示方式:
package main
import "fmt"
func main() {
var numbers []int
for i := 0; i < 10; i++ {
numbers = append(numbers, i)
fmt.Printf("%d\tcap=%d\t%v\n", i, cap(numbers), numbers)
}
}
输出:
0 cap=1 [0]
1 cap=2 [0 1]
2 cap=4 [0 1 2]
3 cap=4 [0 1 2 3]
4 cap=8 [0 1 2 3 4]
5 cap=8 [0 1 2 3 4 5]
6 cap=8 [0 1 2 3 4 5 6]
7 cap=8 [0 1 2 3 4 5 6 7]
8 cap=16 [0 1 2 3 4 5 6 7 8]
9 cap=16 [0 1 2 3 4 5 6 7 8 9]
此输出很有意思。 特别是对于调用 cap() 函数所返回的内容。 一切看起来都很正常,直到第 3 次迭代,此时容量变为 4,切片中只有 3 个元素。 在第 5 次迭代中,容量又变为 8,第 9 次迭代时变为 16。
当切片容量不足以容纳更多元素时,Go 的容量将翻倍。 它将新建一个具有新容量的基础数组。 无需执行任何操作即可使容量增加。 Go 会自动扩充容量。 需要谨慎操作。 有时,一个切片具有的容量可能比它需要的多得多,这样你将会浪费内存。
删除项
package main
import "fmt"
func main() {
letters := []string{"A", "B", "C", "D", "E"}
remove := 2
if remove < len(letters) {
fmt.Println("Before", letters, "Remove ", letters[remove])
letters = append(letters[:remove], letters[remove+1:]...)
fmt.Println("After", letters)
}
}
输出:
Before [A B C D E] Remove C
After [A B D E]
此代码会从切片中删除元素。 它用切片中的下一个元素替换要删除的元素,如果删除的是最后一个元素,则不替换。
创建切片的副本
更改切片中的元素时,基础数组将随之更改。但切片副本不会更改基础数组。
Go 具有内置函数 copy(dst, src []Type) 用于创建切片的副本。 你需要发送目标切片和源切片。 例如,你可如下例所示创建一个切片副本:
slice2 := make([]string, 3)
copy(slice2, letters[1:4])