通过 深入对比 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。
有疑问加站长微信联系(非本文作者)