找回密码
 立即注册
搜索
查看: 365|回复: 0

[go基础] 《Go语言入门指南》学习笔记 - 数组与切片

  [复制链接]
匿名
匿名  发表于 2023-2-8 16:11 |阅读模式

数组

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/ 

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|学习笔记

GMT+8, 2024-12-21 22:26 , Processed in 0.030468 second(s), 14 queries , APCu On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表