27

通过 深入对比 Arrays 和 Slices 学习GO

 4 years ago
source link: https://studygolang.com/articles/24502
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

通过 深入对比 Arrays 和 Slices 学习GO

数组定义

package main

import (
    "fmt"
)

func main() {
    var arr [3]int      //int 数组,长度为3 ,并且初始值都为0 
    fmt.Println(arr)    // output:[0 0 0]
}复制代码

因为很简单的基础知识,就不一一解释了。

数组使用

package main

import (
    "fmt"
)

func main() {
    arr := [3]int{12, 78, 50} //int 数组,长度为3 ,并且初始化
    fmt.Println(arr)          // output:[12 78 50]
}复制代码

上面通过简写的方式声明了数组,并且赋值,但并不是需要为所有元素分配值。如下程序:

package main

import (
    "fmt"
)

func main() {
    arr := [3]int{12} //int 数组,长度为3 ,并且初始化一个值
    fmt.Println(arr)          // output:[12 0 0]
}复制代码

上面程序中哪个 arr 是一个长度为3的数组,只给第0为初始化为12,其他值依旧被默认初始化为0。善于“偷懒”的程序员往往是高手,比如你可以忽略数组的长度,让编译器帮我们去设置,如下:

package main

import (
    "fmt"
)

func main() {
    arr := [...]int{12, 78, 99} //int 数组,长度为3 ,并且初始化一个值
    fmt.Println(arr)            // output:[12 0 0]
}复制代码

通过 ... 编译器可以自动找到数组长度,不同长度的数组类型是属于不同类型,比如:

package main

func main() {
    a := [3]int{5, 78, 8}
    var b [5]int
    b = a //cannot use a (type [3]int) as type [5]int in assignment
}复制代码

编译器直接抛出错误: cannot use a (type [3]int) as type [5]int in assignment

Arrays 属于值类型,也就意味着,如果你将数组赋值给一个新的变量,对新的变量进行修改,将不会影响原有的变量。

package main

import "fmt"

func main() {
    a := [3]int{5, 78, 8}
    var b = a
    b[0] = 100
    fmt.Println(a)
    fmt.Println(b)
}复制代码

output:

[5 78 8]
[100 78 8]复制代码

修改了b,a没有被修改,可见数组的赋值是一个值拷贝。

Slice 定义

package main

import (
    "fmt"
)

func main() {
    a := [5]int{76, 77, 78, 79, 80}
    var b []int = a[1:4] // 基于数组 a 创建一个 slice b
    fmt.Println(b)       // [77 78 79]
}复制代码

上面我们创建了一个 slice 基于数组A,开始于数组的第一位结束于数组的第四位(不包含),然后我们看看改变数组a 会发生什么:

package main

import (
    "fmt"
)

func main() {
    a := [5]int{76, 77, 78, 79, 80}
    var b []int = a[1:4] // 基于数组 a 创建一个 slice b
    fmt.Println(b)       // [77 78 79]

    a[1] = 100
    fmt.Println(b) // [100 78 79]
}复制代码

修改了a[1],结果b[1]的值也修改了,可以看到 slice 是对 数组 a 的应用, slice b 的底层存储实际是数组 b。 为什么是这样呢?可以查看文章[深入理解 Slice](https://github.com/lvgithub/go_blog/blob/master/Books/slice.md)

如果我不想基于某个数组去创建 Slice 可以吗,也是可以的:

package main

import (
    "fmt"
)

func main() {
    i := make([]int, 5, 5)
    fmt.Println(i)

    j := []int{}
    j = append(j, 1)
    fmt.Println(j)
}复制代码

上面通过两种方式创建了 Slice ,尤其注意,make 的方式可以设置 slice 的长度和容量。

package main

import (
    "fmt"
)

func main() {
    i := make([]int, 5, 5)
    fmt.Println(i)

    var j []int

    if j == nil {
        fmt.Printf("before append: j is nil\n")
    }
    j = append(j, 1)
    if j == nil {
        fmt.Printf("after append: j is nil\n")
    }
    fmt.Printf("i len:%d  cap:%d \n", len(i), cap(i))
}
复制代码

output:

[0 0 0 0 0]
before append: j is nil
i len:5  cap:5 复制代码

如上程序,Go 为 slice 提供了两个内置的函数 len() cap() 用来获取 slice 的长度和容量, append 来给 slice 扩充数据。不像数组一样直接通过 length 属性来获取长度。 还要一个细节 slice 不是值类型,因此可以通过 j == nil 来判断,数组是值类型如果这样会报错:

package main

func main() {
    i := [2]int{3, 1}
    if i == nil { //cannot convert nil to type [2]int

    }
}复制代码

编译报错: cannot convert nil to type [2]int

内存优化

slice 在一定的情况下,非常有利于我们优化内存,不需要开辟新的内存空间,这时候只要切片在内存中,就无法对被引用的数组进行垃圾回收,所以当我们遇到一个非常大的数组时,而我们只对其中小部分感兴趣,此时我们应该使用 copy 函数生成该 slice 的副本,使得原数组可以被回收,代码如下:

package main

import (
    "fmt"
)

func main() {
    i := []int{1, 3, 4, 56, 3}
    fmt.Println(i)

    j := make([]int, 5, 5)
    copy(j, i)
    fmt.Println(j)
}复制代码

参考

Arrays and Slices 更多内容,欢迎关注我的 Github


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK