跳到主要内容

go的array和slice

1. array定义

array 是一种数据类型的序列,声明时必须指定长度, 且它的长度必须是 int 常量,下面代码无法通过编译。

package main

func main() {

var a int32 = 7
var b [a]int32
}

2. array的长度

 内建函数len可用来计算 array 的长度。array 允许通过下标访问数组里面的元素。因为下标从0开始,故下标最大为len(a)-1

package main

import "fmt"

func main() {

var b [7]int32 = [7]int32{2, 43, 5, 6, 7, 8, 5}

fmt.Printf("第3个元素是: %d", b[3])
}

3. 多维数组

array 嵌套(即数组里面的元素也是数组)即为多维数组,可以将二维数组想象成棋盘(10行9列)。多维数组里每个元素都是数组,且这些数组的长度必须相等。下面例子中,内嵌数组的长度为3,可通过外层数组的下标和内层数组的下标访问多维数组里面的元素。

package main

import "fmt"

func main() {

var a = [2][3]int{{1, 3, 5}, {2, 4, 6}}

var i, j int
for i = 0; i < 2; i++ {
for j = 0; j < 3; j++ {
fmt.Printf("%d\t", a[i][j])
}
fmt.Println()
}
}

4. 按值拷贝

 与 Java 不同,goarray 是按值拷贝,即:

  1. 将数组 a 赋值给数组 b 时,将拷贝 a 的所有内容。

  2. 将数组作为参数传入方法的时候,将拷贝数组所有内容。

 从下面代码的输出可以看到,数组进行了按值拷贝。

import "fmt"

func main() {

var a = [3]int32{1, 2, 3}

fmt.Printf("address of a int32 in main %p", &a)
copyArray(a)

var b = a
fmt.Printf("\naddress of b int32 in main %p", &b)

}

func copyArray(b [3]int32) {

fmt.Printf("\naddress of int32 in func %p", &b)
}

5. slice

slicearray 类似,但它在执行的过程中可以动态变化。事实上,初始化 slice 时会关联一个 arrayslice 可以看作是 array 的视图,他们共享底层的数据。

 通过内建方法 make 可以初始化 slicemake 方法的签名如下, length表示 slice 的长度,通过len方法可以计算得到,capacity表示 slice 底层数组的长度,通过cap方法可以计算得到。

make([]T, length, capacity)

 下面为示例

package main

import (
"fmt"
"reflect"
)

func main() {

var a = make([]int, 20, 30)

fmt.Printf("\n length : %d, capacity :%d , type: %s", len(a), cap(a), reflect.TypeOf(a))
}

6. slicing

go 定义了 slicing 操作,基础语法为:a[low : high],上述例子中,初始化长度为20,容量为30的 slice 可以修改为:

new([30]int)[0:20]

 先定义一个长度为30的数组,然后再进行 slicing 操作。slicing 得到一个起点为0,长度为high - low,容量为capacity - low的结果,例如:

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

s[0] == 2
s[1] == 3
s[2] == 4

 为了使用方便,lowhigh可以省略。当low省略时,默认取0;当high省略时,默认取被切的操作数的长度。例如:

a[2:]  // same as a[2 : len(a)]
a[:3] // same as a[0 : 3]
a[:] // same as a[0 : len(a)]
提示

a 为执行数组的指针时,a[low,high]相当于(*a)[low,high]。实际上new([30]int32)返回的是指针。

slicing 操作也可以作用于 slice,得到的 slice 和底层数组共享内存。例如:

package main

import "fmt"

func main() {

var a [10]int32 = [10]int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

var slice1 = a[1:5] // slice1[0] 与 a[1]对应

var slice2 = slice1[2:4] // slice2[0] 、slice1[2]、a[3] 对应

fmt.Printf("\n a[3] 的地址:%p", &a[3])
fmt.Printf("\n slice1[2] 的地址:%p", &slice1[2])
fmt.Printf("\n slice2[0] 的地址:%p", &slice2[0])
}

 事实上,slicing 完整的语法为:a[low : high : max],相比于a[low : high]a[low : high : max]可以将结果的容量设置为:max - low,但只允许省略low

7. slice内部结果

slice 结构体包含三部分:指向底层数组的指针;长度;容量。

 当执行make([] byte,5)时,结构如下:

 执行s = s[2:4]时,结构如下:

8. slice动态增长

 与 array 不同,slice 可以动态增长。内建函数append可以向 slice 里添加元素,当超过 slice 的容量后,slice 的容量会自动增长。

package main

import "fmt"

func main() {

a := make([]int32, 1, 2) //声明一个容量为2的slice

fmt.Printf("\na的长度:%d,a的容量:%d\n", len(a), cap(a))

a = append(a, 1, 2, 3)

fmt.Println(a)
fmt.Printf("a的长度:%d,a的容量:%d", len(a), cap(a))
}

append支持将 slice2 添加到 slice1,但需要使用...slice2 展开。

a := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
// a == []string{"John", "Paul", "George", "Ringo", "Pete"}

  1. Go Slices: usage and internals

署名-非商业性使用-禁止演绎 4.0 国际