golang 数组、切片(slice)

数组

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组。

var a [3]int
var b [3]int = [3]int{1, 2, 3}

func main() {
    fmt.Println(a[0])
    fmt.Println(b[1])
}

在数组字面值中, 如果在数组的长度位置出现的是“…”省略号, 则表示数组的长度是根据初始化值的个数来计算:

var d = [...]int{1, 2, 3, 4}

func main() {
    c := [...]int{1, 2, 3}
    fmt.Println(c[1])
}

数组的长度是数组类型的一个组成部分, 因此[3]int和[4]int是两种不同的数组类型。


指定一个索引和对应值列表的方式初始化:

type Currency int

const (
    USD Currency = iota
    ERU
    RMB
)

var symbol = [...]string{USD: "$", ERU: "@", RMB: "¥"}

func main() {
    fmt.Println(symbol[RMB])
}

没用到的索引可以省略, 和前面提到的规则一样, 未指定初始值的元素将用零值初始化。例如:

r := [...]int{99: 1000}

定义了一个含有100个元素的数组r, 最后一个元素被初始化为1000, 其它元素都是用0初始化。

数组比较

只有当两个数组的所有元素都是相等的时候数组才是相等的。

a := [2]int{1, 2}
b := [...]int{1, 2}
fmt.Println(a == b) // true

slice切片

Slice( 切片) 代表变长的序列, 序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像, 只是没有固定长度而已。

一个slice由三个部分构成: 指针、 长度和容量。指针指向第一个slice元素对应的底层数组元素的地址;长度对应slice中元素的数目; 容量一般是从slice的开始位置到底层数据的结尾位置;内置的len和cap函数分别返回slice的长度和容量。


多个slice之间可以共享底层的数据, 并且引用的数组部分区间可能重叠。下面两个slice都包含了六月份:

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

func main() {
    b := a[2:4]
    fmt.Println(b) // [3 4]
}

因为slice值包含指向第一个slice元素的指针, 因此向函数传递slice将允许在函数内部修改底层数组的元素。

扫描二维码关注公众号,回复: 1929986 查看本文章
func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

func main() {
    // 数组
    a := [...]int{0, 1, 2, 3, 4, 5}
    b := [...]int{0, 1, 2, 3}

    // 反转切片
    reverse(a[:])
    fmt.Println(a) //[5 4 3 2 1 0]
    reverse(b[:])
    fmt.Println(b) //[3 2 1 0]
}

slice类型和数组类型的初始化语法差异:slice并没有指明序列的长度。

func main() {
    s := []int{0, 1, 2, 3, 4, 5}
    reverse(s)
    fmt.Println(s) // "[2 3 4 5 0 1]
}

如果你需要测试一个slice是否是空的, 使用len(s) == 0来判断。


内置的make函数创建一个指定元素类型、 长度和容量的slice。 容量部分可以省略, 在这种情况下, 容量将等于长度。

make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]

在底层, make创建了一个匿名的数组变量, 然后返回一个slice;只有通过返回的slice才能引用底层匿名的数组变量。在第一种语句中, slice是整个数组的view。 在第二个语句中, slice只引用了底层数组的前len个元素。

append函数

append函数对于理解slice底层是如何工作的非常重要,下面是第一个版本的appendInt函数, 专门用于处理[]int类型的slice:

func appendInt(x []int, val int) []int {
    var z []int
    zlen := len(x) + 1
    if zlen <= cap(x) {
        z = x[:zlen]
    } else {
        zcap := zlen
        if zcap < 2*len(x) {
            zcap = 2*len(x)
        }

        z = make([]int, zlen, zcap)
        copy(z, x)
    }

    z[len(x)] = y
    return z
}
  1. 检测slice底层数组是否有足够的容量来保存新添加的元素,如果有足够空间的话, 直接扩展slice。
  2. 如果没有足够的增长空间的话, appendInt函数则会先分配一个足够大的slice空间z
  3. 将输入的x复制到新的空间z, 然后添加y元素

内置的append函数可能使用比appendInt更复杂的内存扩展策略。因此我们并不知道append调用是否导致了内存的重新分配,也不能确认新的slice和原始的slice是否引用的是相同的底层数组空间。

因此, 通常是将append返回的结果直接赋值给输入的slice变量。

runes = append(runes, r)

猜你喜欢

转载自blog.csdn.net/andybegin/article/details/80942349