数组
0x01 什么是数组
数组是具有相同 唯一类型 的一组已编号且长度固定的数据项序列(这是一种同构的数据结构);
这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
数组声明的格式:
1
|
var identifier [len]type // 声明时必须给出数组长度,因为编译时根据长度分配内存
|
示例
1
2
|
var arr [5]int
arr[i] = value // 对索引项为 i 的元素赋值
|
遍历数组元素,使用 for 结构:
- 通过 for 初始化数组项
- 通过 for 打印数组元素
- 通过 for 依次处理元素
1
2
3
4
5
6
7
8
|
// 方法1
for i:=0; i < len(arr1); i++{
arr1[i] = ...
}
// 方法2
for i,_:= range arr1 {
...
}
|
Go 语言中的数组是一种 值类型,所以可以通过 new()来创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// arr1 的类型是 *[5]int
var arr1 = new([5]int) //
// arr2 的类型是 [5]int
var arr2 [5]int
// 把 arr1 赋给 arr2 ,需要再做一次内存的拷贝
arr2 := *arr1
arr2[2] = 100
// 函数中数组作为参数传入
func1(arr2) // 产生一次数组拷贝,func1 方法不会修改原始数组 arr2
func1(&arr2) // 修改原始数组 arr2 ,通过 &arr2 引用方式传入
// 注:通过生成数组切片,也可以修改原始数组
|
0x02 数组常量
如果数组的值已经知道了,那么可以通过 数组常量 的方法来初始化数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import "fmt"
func main() {
var arrAge = [5]int{18, 20, 15, 22, 16}
var arrLazy = [...]int{5, 6, 7, 8, 22} // 变成了切片
var arrLazy = []int{5, 6, 7, 8, 22}
var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}
var arrKeyValue = []string{3: "Chris", 4: "Ron"}
for i:=0; i < len(arrKeyValue); i++ {
fmt.Printf("Person at %d is %s\n", i, arrKeyValue[i])
}
}
|
几何点(或者数学向量)是一个使用数组的经典例子。
为了简化代码通常使用一个别名:
1
2
|
type Vector3D [3]float32
var vec Vector3D
|
0x03 多维数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
const (
WIDTH = 1920
HEIGHT = 1080
)
type pixel int
var screen [WIDTH][HEIGHT]pixel
func main() {
for y := 0; y < HEIGHT; y++ {
for x := 0; x < WIDTH; x++ {
screen[x][y] = 0
}
}
}
|
0x04 将数组传递给函数
将一个大的数组传递给函数会消耗很多内存,有两种方法避免:
传递数组的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
func main() {
array := [3]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Note the explicit address-of operator
// to pass a pointer to the array
fmt.Printf("The sum of the array is: %f", x)
}
func Sum(a *[3]float64) (sum float64) {
for _, v := range a { // derefencing *a to get back to the array is not necessary!
sum += v
}
return
}
|
切片
0x01 什么是切片?
- 是对数组一个连续片段的引用,即切片是数组的一个片段
- 切片是引用类型
- len() 获取切片长度
- cap() 获取切片容量,从第一个元素开始,到相关数组末尾的元素个数
- 0 <= len(s) <= cap(s)
- 切片的长度在运行时可以修改
- 切片是一个 长度可变数组
多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。
优点:切片是引用,比使用数组效率高
切片声明格式
1
|
var identifier []type // 不需要说明长度
|
一个切片在未初始化之前默认为 nil,长度为 0。
切片初始化格式
1
2
3
4
|
var slice1 []type = arr1[start:end] // 切片 从 arr1 start ~ end 截取 数组
var slice1 []type = arr1[:] // 切片等于完整的 arr1 数组,arr1[0:len(arr1)] 缩写
slice1 = &arr1
|
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main
import "fmt"
func main() {
var arr1 [6]int
var slice1 []int = arr1[2:5] // item at index 5 not included!
// load the array with integers: 0,1,2,3,4,5
for i := 0; i < len(arr1); i++ {
arr1[i] = i
}
// print the slice
for i := 0; i < len(slice1); i++ {
fmt.Printf("Slice at %d is %d\n", i, slice1[i])
}
fmt.Printf("The length of arr1 is %d\n", len(arr1)) // The length of arr1 is 6
fmt.Printf("The length of slice1 is %d\n", len(slice1)) // The length of slice1 is 3
// 从 2 ~ 6 为 切片的容量
fmt.Printf("The capacity of slice1 is %d\n", cap(slice1)) // The capacity of slice1 is 4
// grow the slice
slice1 = slice1[0:4]
for i := 0; i < len(slice1); i++ {
fmt.Printf("Slice at %d is %d\n", i, slice1[i])
}
fmt.Printf("The length of slice1 is %d\n", len(slice1))
fmt.Printf("The capacity of slice1 is %d\n", cap(slice1))
// grow the slice beyond capacity
//slice1 = slice1[0:7 ] // panic: runtime error: slice bound out of range
}
|
注意:
如果 s2 是一个 slice,你可以将 s2 向后移动一位 s2 = s2[1:],但是末尾没有移动。
绝对不要用指针指向 slice。切片本身已经是一个引用类型,所以它本身就是一个指针!!
0x02 将切片传递给函数
1
2
3
4
5
6
7
8
9
10
11
12
|
func sum(a []int) int {
s := 0
for i := 0; i < len(a); i++ {
s += a[i]
}
return s
}
func main() {
var arr = [5]int{0, 1, 2, 3, 4}
sum(arr[:])
}
|
0x03 用 make() 创建一个切片
当相关数组还没有定义时,用 make() 创建一个切片。
1
2
3
4
|
var slice1 []type = make([]type, len)
// 简写
slice1 := make([]type, len) // len 为数组的长度,也是 slice 的初始长度
slice1 := make([]type, len, cap)
|
下面两种方法生成相同的切片
1
2
|
make([]int, 50, 100)
new([100]int)[0:50]
|
0x04 new() 和 make() 的区别
- new (T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 * T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{}。
- make(T) 返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:切片、map 和 channel
0x05 bytes 包
类型 []byte 的切片十分常用。
Buffer 定义方式:
1
2
3
4
5
6
7
8
9
|
// 方式1
var buffer bytes.Buffer
// 方式2
var r *bytes.Buffer = new(bytes.Buffer)
// 方式3
func NewBuffer(buf []byte) *Buffer {
}
|
通过 buffer.WriteString(s) 串联字符串 比 += 省CPU和内存
1
2
3
4
5
6
7
8
9
|
var buffer bytes.Buffer
for {
if s, ok := getNextString(); ok { //method getNextString() not shown here
buffer.WriteString(s) // 追加
} else {
break
}
}
fmt.Print(buffer.String(), "\n")
|
0x06 For-range 结构
遍历切片/数组
1
2
3
|
for ix, value := range slice1 {
...
}
|
- 第一个返回值ix:数组或者切片的索引
- 第二个返回值value:该索引位置的值,
- ix,value 都是循环内部可见的局部变量
- value 是 slice 某个索引位置的值的一个拷贝
多维数组/切片下的for-range
1
2
3
4
5
|
for row := range screen {
for column := range screen[row] {
screen[row][column] = 1
}
}
|
0x07 切片重组
改变切片长度的过程称之为切片重组 reslicing,
做法:
1
2
3
4
5
6
7
|
// 创建切片
// start_length: 切片初始长度
// capacity : 相关数组的长度
slice1 := make([]type, start_length, capacity)
// 切片扩容(重组)
slice1 = slice1[0:end] // end 是新的末尾索引(即长度)
|
0x08 切片 复制 与 追加
- 切片复制:func copy(dst, src []T) int 将类型为T的切片从源地址 src 拷贝到目标地址 dst ,覆盖dst相关元素,返回拷贝的元素个数
- 切片追加:func append(s[]T, x ...T) []T 将0个或多个具有相同类型s的元素追加到切片后面,返回新的切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import "fmt"
func main() {
sl_from := []int{1, 2, 3}
sl_to := make([]int, 10)
n := copy(sl_to, sl_from) // 切片copy
fmt.Println(sl_to)
fmt.Printf("Copied %d elements\n", n) // n == 3
sl3 := []int{1, 2, 3}
sl3 = append(sl3, 4, 5, 6) // 切片追加
fmt.Println(sl3)
}
|
原文地址:http://www.manoner.com/post/GoLand/Go%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E6%95%B0%E7%BB%84%E4%B8%8E%E5%88%87%E7%89%87/
|