在本系列的第四篇文章中,我将解释 Go数组和切片,包括如何使用它们,以及为什么你通常要选择其中一个而不是另一个。

数组

数组是编程语言中最流行的数据结构之一,主要原因有两个:一是简单易懂,二是可以存储许多不同类型的数据。

anArray 
 anArray := [4]int{-1, 2, 0, -4}
  
len 
for for range 

最后,你也可以定义一个 二维数组 ,如下:

 twoD := [3][3]int{
  {1, 2, 3},
  {6, 7, 8},
  {10, 11, 12}}
  
arrays.go 
 for i := 0; i 
  for j := 0; j   
for range arrays.go 
arrays.go 
 $ go run arrays.go
Before change: [-1 2 0 -4]
After change: [-1 2 0 -4]
1 2 3
6 7 8
10 11 12
1 2 3
6 7 8
10 11 12
  

这个输出告诉我们:对函数内的数组所做的更改,会在函数退出后丢失。

数组的缺点

Go 数组有很多缺点,你应该重新考虑是否要在 Go 项目中使用它们。

首先,数组定义之后,大小就无法改变,这意味着 Go 数组不是动态的。简而言之,如果你需要将一个元素添加到一个没有剩余空间的数组中,你将需要创建一个更大的数组,并将旧数组的所有元素复制到新数组中。

其次,当你将数组作为参数传递给函数时,实际上是传递了数组的副本,这意味着你对函数内部的数组所做的任何更改,都将在函数退出后丢失。

最后,将大数组传递给函数可能会很慢,主要是因为 Go 必须创建数组的副本。

以上这些问题的解决方案,就是使用 Go 切片。

切片

Go 切片与 Go 数组类似,但是它没有后者的缺点。

append 
capacity length len cap 

由于切片的大小是动态的,如果切片空间不足(也就是说,当你尝试再向切片中添加一个元素时,底层数组的长度恰好与容量相等),Go 会自动将它的当前容量加倍,使其空间能够容纳更多元素,然后将请求的元素添加到底层数组中。

此外,切片是通过引用传递给函数的,这意味着实际传递给函数的是切片变量的内存地址,这样一来,你对函数内部的切片所做的任何修改,都不会在函数退出后丢失。因此,将大切片传递给函数,要比将具有相同数量元素的数组传递给同一函数快得多。这是因为 Go 不必拷贝切片 —— 它只需传递切片变量的内存地址。

slice.go 
 package main

 import  (
  "fmt"
)

func negative(x []int) {
  for i, k := range x {
    x  = -k
  }
}

func printSlice(x []int) {
  for _,  number  := range x {
    fmt.Printf("%d ", number)
  }
  fmt.Println
}

func main {
  s := int{0, 14, 5, 0, 7, 19}
  printSlice(s)
  negative(s)
  printSlice(s)

  fmt.Printf("Before. Cap: %d, length: %d\n", cap(s), len(s))
  s = append(s, -100)
  fmt.Printf("After. Cap: %d, length: %d\n", cap(s), len(s))
  printSlice(s)

  anotherSlice := make([]int, 4)
  fmt.Printf("A new slice with 4 elements: ")
  printSlice(anotherSlice)
}
   
append  append  printSlice negative 
slice.go 
 $ go run slice.go
0 14 5 0 7 19
0 -14 -5 0 -7 -19
Before. Cap: 6, length: 6
After. Cap: 12, length: 7
0 -14 -5 0 -7 -19 -100
A new slice with 4 elements: 0 0 0 0
  
int 

使用切片来引用数组

[:] refArray.go [:] 
refArray.go 
 func main {
  anArray := [5]int{-1, 2, -3, 4, -5}
  refAnArray := anArray[:]

  fmt.Println("Array:", anArray)
  printSlice(refAnArray)
  negative(refAnArray)
  fmt.Println("Array:", anArray)
}
  
refArray.go 
 $ go run refArray.go
Array: [-1 2 -3 4 -5]
-1 2 -3 4 -5
Array: [1 -2 3 -4 5]
  
anArray 

总结

尽管 Go 提供了数组和切片两种类型,你很可能还是会使用切片,因为它们比 Go 数组更加通用、强大。只有少数情况需要使用数组而不是切片,特别是当你完全确定元素的数量固定不变时。

arrays.go slice.go refArray.go 

如果你有任何问题或反馈,请在下方发表评论或在 Twitter 上与我联系。

via:

本文由 LCTT原创编译, Linux中国 荣誉推出