1. 前言
go中的数组不同于C、php,没有那么灵活,但是拥有切片的类型。一般开发上,常用切片,下面我们就来探索下它们的奥妙。
2. 数组
go数组是同一种数据类型的固定长度的数据结构,其数组长度也是数组类型的一部分。
声明格式:
var identifier [len]type
例如:
var arr [8]int
var brr [8]string
如上 arr 的长度是 5,类型是int,索引范围从 0 到 7 【len(arr)-1】
数组是可变的。对索引项为 i 的数组元素赋值可以这么操作:arr[i] = value,
如果索引超出数组最大范围,会触发访问越界报错
内置函数 len 和 cap 都可返回数组长度 (元素数量)。
runtime error: index out of range
2.1. 数组初始化
- 未初始化元素值为 0
var a = [5]int{1, 2}
a := [5]int{1, 2}
- 通过...可忽略数组长度(其数组长度由值确定),相当于切片。
b := [...]int{1, 2, 3}
- 使用索引号初始化元素。
c := [5]int{1: 80, 4: 50}
- 使用索引号赋值。
var d [5]int
d[3] = 10
- 多维数组
var dr1 [2][3]int
var dr2 = [...][3]int{ {1, 2, 3}, {6, 7, 8} } // 第二维度不能用 "..."
3. 切片
切片的长度是不固定的,是对数组一个连续片段的引用,是一个长度可变的数组。
声明切片的格式是:
var identifier []type(不需要说明长度)
从声明中可看出切片和数组明显区别在([]里不需要len),切片不需要指定长度,这样切片就不会使用额外的内存, 效率会比数组大,所以在go项目开发中切片比数组更常用。
3.1. 切片初始化
切片的初始化格式是:
var slice []type = arr[start:end]
slice在未初始化之前默认为 nil,长度为 0。
slice 是由数组 arr 从 start 索引到 end-1 索引之间的元素构成的子集,(slice1[0] = arr[start])
若 var slice []type = arr[:],slice 就等于完整的 arr 数组,是 arr[0:len(arr)] 的一种缩写,还可直接 slice = &arr
cap可测量出切片的容量,它等于切片从第一个元素开始,到相关数组末尾的元素个数。
切片的长度不会超过它的容量,0 <= len(slice) <= cap(slice) 永远成立。
如:
package main
import (
"fmt"
)
func main() {
var arr = [5]int{0, 1, 2, 3, 4}
a := arr[:] // arr[0:len(arr)]的简化写法
b := arr[:1] // arr[0:1]的简化写法
c := arr[:len(arr)-1] //去掉切片的最后一个元素
fmt.Print(a, b, c)
}
输出:[0 1 2 3 4] [0] [0 1 2 3]
3.2. 用make来创建切片
当相关数组还没有定义时,我们可以使用 make() 函数来创建一个切片,同时创建好相关数组:var slice []type = make([]type, len)
- 可简化成 slice := make([]type, len)
package main
import "fmt"
func main() {
slice := make([]int, 10)
for i := 0; i < len(slice); i++ {
slice[i] = 2 * i
}
for i := 0; i < len(slice); i++ {
fmt.Printf(" slice[%d] = %d\n", i, slice[i])
}
fmt.Printf("len is %d\n", len(slice))
fmt.Printf("cap is %d\n", cap(slice))
}
输出:
slice[0] = 0
slice[1] = 2
slice[2] = 4
slice[3] = 6
slice[4] = 8
slice[5] = 10
slice[6] = 12
slice[7] = 14
slice[8] = 16
slice[9] = 18
len is 10
cap is 10
3.3. append追加切片元素
func append(slice []Type, elems ...Type) []Type
向 slice 尾部添加数据,返回新的 slice 对象。
package main
import "fmt"
func main() {
a := []int{1, 2}
b := []int{3, 4}
c := append(a, b...)
d := append(c, 5)
e := append(d, 6, 7)
fmt.Printf("slice a : %v\n", a)
fmt.Printf("slice b : %v\n", b)
fmt.Printf("slice c : %v\n", c)
fmt.Printf("slice d : %v\n", d)
fmt.Printf("slice e : %v\n", e)
}
输出:
slice a : [1 2]
slice b : [3 4]
slice c : [1 2 3 4]
slice d : [1 2 3 4 5]
slice e : [1 2 3 4 5 6 7]
3.4. 切片拷贝
Go语言的内置函数copy()可以将一个数组切片复制到另一个数组切片
copy(dst, src []Type) int
copy函数的第一个参数是要复制的目标slice,第二个参数是源slice,函数会返回成功复制的元素的个数。
如果两个切片不一样大,就会以长度小的切片元素个数进行复制。两个 slice 可指向同一底层数组,允许元素区间重叠。
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3}
fmt.Printf("slice a : %v\n", a)
b := make([]int, 5)
fmt.Printf("slice b : %v\n", b)
copy(b, a)
fmt.Printf("copied slice a : %v\n", a)
fmt.Printf("copied slice b : %v\n", b)
c := []int{4, 5}
fmt.Printf("slice c : %v\n", c)
copy(c, b)
fmt.Printf("copied slice c : %v\n", c)
c = append(c, 6, 7, 8)
fmt.Printf("last slice c : %v\n", c)
}
输出结果:
slice a : [1 2 3]
slice b : [0 0 0 0 0]
copied slice a : [1 2 3]
copied slice b : [1 2 3 0 0]
slice c : [4 5]
copied slice c : [1 2]
last slice c : [1 2 6 7 8]
3.5. 字符串和切片
string可看作是一个byte的数组,可以进行切片操作。
package main
import (
"fmt"
)
func main() {
str := "hello world"
a := str[0:5]
b := str[6:]
fmt.Println(a)
fmt.Println(b)
}
输出:
hello
world
字符串是不可变的,它不能像数组切片那样可以直接通过索引赋值去改变,需要操作可转化,如下:
package main
import (
"fmt"
)
func main() {
a := "hello world"
s := []byte(a) // 含有中文字符需要用[]rune(a)
s[0] = 'H'
s[6] = 'W'
s = append(s, '!')
a = string(s)
fmt.Println(a)
// 含有中文字符
b := "我是中国人 I'm proud"
z := []rune(b)
z[5] = '!'
z = z[:6]
b = string(z)
fmt.Println(b)
}
输出:
Hello World!
我是中国人!
3.6. slice遍历
package main
import (
"fmt"
)
func main() {
data := []int{1, 2, 3, 4, 5}
for index, value := range data {
fmt.Printf("inde : %v , value : %v\n", index, value)
}
}
输出:
inde : 0 , value : 1
inde : 1 , value : 2
inde : 2 , value : 3
inde : 3 , value : 4
inde : 4 , value : 5