七叶笔记 » golang编程 » 对比着学 Go 语言-基础:类型之数组

对比着学 Go 语言-基础:类型之数组

数组是 Go 语言编程中最常用的数据结构。

数组的常规声明方法有:

 [32]byte                        // 长度为 32 的数组,每个元素为一个字节
[2*N] struct {x, y int32} // 复杂类型数组
[1000] * float64            // 指针数组
[3][5]int                        // 二维数组
[2][2][2]float64            //  等同于[2]([2]([2]float64))  

在 Go 语言中, 数组长度在定义后就不可更改,在声明时长度可以为一个常量或者一个常量表达式(常量表达式是指在编译期即可计算结果的表达式)。

数组的长度是该数组类型的一个内置常量,可以用 Go 语言的内置函数 len() 来获取。

 arrlength := len(arr)  

数组元素的访问:

 for i := 0; i<len(array); i++ {
    fmt.Println("Element", i, "of array is", array[i]) 
}  

Go 和 Python 一样也有关键字 range, 它的功能就是遍历容器中的元素。数组也是 range 的支持范围之一。

 for i, v := range array {
    fmt.Println("Array element[", i, "]=", v) 
}  

range 具有两个返回值,第一个返回值是元素的数组下标,第二个返回值是元素的值。

数组内值类型

值类型的概念有 1 个特点:

  • 在赋值和作为参数传递时,将产生一次复制动作。
 package main
import "fmt"

func modify(array [5]int){
     array[0] = 10 //试图修改数组的第一个元素
     fmt.Println("In modify(), array values:", array)
}

# 函数 modify 内操作的那个数组跟 main 中传入的数组是两个不同的实例。
func main(){
     array := [5]int{1, 2, 3, 4, 5} // 定义并初始化一个数组
     modify(array)  //传递给一个函数,并试图在函数体内修改这个数组内容
     fmt.Println("In main(), array values:", array)
}  

Go 语言和 Python 非常相似的地方就是数组切片操作。

数组的特点是:

  1. 长度在定义之后无法再次修改
  2. 每传递一次都会产生一份副本。

而切片作为指向数组的指针,同时拥有:

  1. 指向原数组的指针
  2. 元素个数
  3. 已分配的存储空间

这是一个完整的数据结构。只是在底层仍然使用数组来管理元素。

数组切片基于数组添加了一系列管理功能,可以随时动态扩充存放空间,并且可以被随意传递而不会导致所管理的元素被重复复制。

下面从 3 个方面来考察数组切片:

  • 创建数组切片
  • 基于数组切片创建数组切片
  • 元素遍历
  • 动态增减元素
  • 内容复制

创建

 package main
import "fmt"

func main() {
  var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  var mySlice []int = myArray[:5]
  
  fmt.Println("Elements of myArray: ")
  for _, v := range myArray {
      fmt.Print(v, " ") 
  }
  
  fmt.Println("\n Elements of mySlice: ")
  for _, v := range mySlice {
      fmt.Print(v, " ")
  }
  
  fmt.Println()
  
}  

可以看到,这里切片的操作和 Python 是一模一样的。

 mySlice = myArray[:]
mySlice = myArray[:5]
mySlice = myArray[5:]  

以上的方式都是基于数组来创建的,下面是通过元素直接创建:

 mySlice1 := make([]int, 5) // 创建一个初始元素个数为 5 的数组切片,元素初始值为 0
mySlice2 := make([]int, 5, 10) // 创建一个初始元素个数为 5 的数组切片,元素初始值为 0, 并预留 10 个元素的存储空间
mySlice3 := []int{1, 2, 3, 4, 5}  

这中间是会有匿名数组被创建出来的,但不需要操心。

还可以基于数组切片创建数组切片,和基于数组创建切片的不同之处在于,数组定长,数组切片不定长。也是基于这个特点,新的切片可以选择到旧切片的范围外,范围外的元素在新切片中会被填上 0 。

 oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:6]  

遍历

数组切片可以兼容所有操作数组的方法。

 # 数组切片的遍历
for i := 0; i < len(mySlice); i++ {
    fmt.Println("mySlice[", i, "] =", mySlice[i])
}  

 for i, v := range mySlice {
    fmt.Println("mySlice[", i, "] = ", v) 
}  

动态增减

与数组相比,数组切片多了一个存储能力的概念,其元素个数和分配的空间可以是两个不同的值。

 package main
import "fmt"

func main() {
   mySlice := make([]int, 5, 10)
  
  fmt.Println("len(mySlice):", len(mySlice)) // len 返回的是数组切片中当前所存储元素个数
  fmt.Println("cap(mySlice):", cap(mySlice)) // cap 返回的数组切片分配的空间大小
}  

增加的方法和像数组中追加元素的方法一样,使用 append() 函数, 它的第二个参数是一个不定参数。

 mySlice = append(mySlice, 1, 2, 3)  

 mySlice2 := []int{8, 9, 10}
mySlice = append(mySlice, mySlice2...)  

和 Python 一样,Go 也存在省略号。相当于把 mySlice2 包含的所有元素打散后传入。等同于下面:

 mySlice = append(mySlice, 8, 9, 10)  

数组切片会自动处理存储空间不足的问题。

内容复制

和 Python 一样,Go 也存在内置函数 copy(), 它执行的是将一个切片复制到另一个切片。当两个切片大小不一,却要执行复制操作时,就会按照较小的那个切片个数进行复制。

 slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}

copy(slice2, slice1) // 只会复制 slice1 的前 3 个元素到 slice2 中
copy(slice1, slice2) // 只会复制 slice2 的 3 个元素到 slice1 的前 3 个位置  

相关文章