Go:数组和切片,深入研究。

2025-06-08

Go:数组和切片,深入研究。

欢迎,

在本文中,我们将全面探讨 Go 中数组和切片的基础知识,深入探讨它们的内部结构,以及为什么它们虽然功能相似,但行为却截然不同。我们将分 5 个主题来探讨数组和切片及其行为和区别:

  • 默认值或“零”值
  • 声明和初始化数组和切片
  • 数组和切片的值部分
  • 操作数组和切片
  • 切片的潜在怪癖
  • 优化代码性能的技巧

注意事项

  • len是一个内置函数,返回切片数组中的项目数
  • cap是一个内置函数,用于返回数组或切片的容量。数组的容量等于其长度,而切片的容量是切片在需要调整大小之前可以容纳的最大元素数(切片容量可能大于其长度)。
  • fmt.Println是 Go 包中的一个函数fmt,用于将一行打印到标准输出。它接受一个或多个值作为参数,并将它们打印到控制台,每个值之间用空格分隔,后跟一个换行符。

默认值或“零”值

在 Go 中,如果声明一个变量而没有显式初始化,该变量会被自动设置为其类型的零值。零值是声明特定类型的变量但未显式初始化时赋予该变量的默认值。例如,如果像这样声明一个 int 变量:



var x int


Enter fullscreen mode Exit fullscreen mode

x 的值将被初始化为 0。
在其他语言(如 Javascript)中,未初始化变量的值是undefined

每种类型的零值如下:

  • int:0
  • float:0.0
  • bool: 错误的
  • string: ""(空字符串)
  • pointer:零
  • struct:所有字段均设置为其各自类型的零值

Go 中数组的零值是指所有元素都设置为其对应类型的零值的数组。例如,假设有一个整数数组:



var arr [5]int


Enter fullscreen mode Exit fullscreen mode

该数组的零值为:



[0, 0, 0, 0, 0]


Enter fullscreen mode Exit fullscreen mode

类似地,如果你有一个字符串数组:



var arr [5]string


Enter fullscreen mode Exit fullscreen mode

该数组的零值为:



["", "", "", "", ""]


Enter fullscreen mode Exit fullscreen mode

在 Go 中,切片的零值为 nil,即长度为 0、容量为 0 且没有底层数组的切片。例如:



var slice []int
fmt.Println(slice == nil) // => true


Enter fullscreen mode Exit fullscreen mode

声明和初始化数组

Go 中声明数组的格式是var name [L]T.
var是 Go 中用于声明各种变量的关键字
name是变量的名称,可以是任意值
L是数组的长度(必须是常量)T是数组项的类型。
我们来看一些例子



//Array of 5 Intergers
var nums [5]int
fmt.Println(nums) // => [0 0 0 0 0]

//Array of 10 strings
var strs [10]string
fmt.Println(nums) // => [         ]

// Nested arrays
var nested = [3][5]int{
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10},
    {11, 12, 13, 13, 15},
}
fmt.Println(nested) // => [[1 2 3 4 5] [6 7 8 9 10] [11 12 13 13 15]]


Enter fullscreen mode Exit fullscreen mode

初始化数组只是为变量分配一个值,
var name = [L]T{...}其中...表示数组项的类型T



//Intializing an array containing 10 intergers
var nums = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(nums) // => [1 2 3 4 5 6 7 8 9 10]

//Intializing an array containing 10 strings
var strs = [10]string{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}
fmt.Println(strs) // => [one two three four five six seven eight nine ten]

//Nested arrays
var nested = [3][2]int{}


Enter fullscreen mode Exit fullscreen mode

您还可以创建一个结构数组



type Car struct {
    Brand string
    Color string
    Price float32
}

//Array of 5 items of type Car
var arrayOfCars = [5]Car{
    {Brand: "Porsche", Color: "Black", Price: 20_000.00},
    {Brand: "Volvo", Color: "White", Price: 8_000.00},
    {Brand: "Honda", Color: "Blue", Price: 7_000.00},
    {Brand: "Tesla", Color: "Black", Price: 50_000.00},
    {Brand: "Kia", Color: "Red", Price: 5_000.98},
}
fmt.Println(arrayOfCars) // => [{Porsche Black 20000} {Volvo White 8000} {Honda Blue 7000} {Tesla Black 50000} {Kia Red 5000.98}]


Enter fullscreen mode Exit fullscreen mode

在 Go 中,要创建包含不同类型的元素的数组,可以使用interface{}类型。Go
中的接口是一种类型,它定义了一组类型必须实现的方法。任何实现了接口中列出的所有方法的类型都被称为满足该接口,并被视为属于该接口类型。特殊接口类型interface{}没有方法,这意味着 Go 中的每种类型都满足此接口。



package main

import "fmt"

func main() {
    //Array containing 5 items of different type
    var randomsArray = [5]interface{}{"Hello world!", 35, false, 33.33, 'A'}
    fmt.Println(randomsArray) // => [Hello world! 35 false 33.33 65]

}


Enter fullscreen mode Exit fullscreen mode

初始化数组的其他方法



import "fmt"

func main() {
    // Using shorthand syntax
    cars := [3]string{"Tesla", "Ferrari", "Benz"}
    fmt.Println(cars) // => [Tesla Ferrari Benz]

    // Using ... inplace of array length
    digits := [...]int{10, 20, 30, 40}
    fmt.Println(digits) // => [10 20 30 40]

    // Using len keyword
    countries := [len(digits)]string{"China", "India", "Kenya"}
    fmt.Println(countries) // => [China India Kenya]
}


Enter fullscreen mode Exit fullscreen mode

请注意,您不能:=在全局范围内使用简写语法

声明和初始化切片

要声明切片,我们使用格式var name []int,声明数组和切片之间的唯一区别是,对于切片,我们省略了长度。
示例



import "fmt"

func main() {
    // A slice of intergers
    var intSlice []int
    fmt.Println(intSlice) // => []

    // A slice of intergers
    var stringSlice []string
    fmt.Println(stringSlice) // => []
}


Enter fullscreen mode Exit fullscreen mode

要在 Go 中初始化切片,可以使用 make 函数。make 函数接受三个参数:切片的类型、切片的长度和切片的容量(可选)。make([]T, len, cap)

例如,要创建一个长度为 5、容量为 10 的整数切片,可以使用以下代码:



package main

import "fmt"

func main() {
    // With capacity
    slice1 := make([]int, 5, 10)
    fmt.Println(len(slice1), cap(slice1)) // => 5 10

    // Without capacity
    slice2 := make([]int, 5)
    fmt.Println(len(slice2), cap(slice2)) // => 5 5
}


Enter fullscreen mode Exit fullscreen mode

当省略容量时,容量将设置为切片的长度。

make您还可以通过立即为切片赋值来初始化切片,而无需使用函数



slice := []int{1, 2, 3}
fmt.Println(len(slice), cap(slice)) // => 3 3


Enter fullscreen mode Exit fullscreen mode

数组和切片的值部分

这是 Go 中数组和切片之间最重要的区别:数组只有一个部分,而切片有直接部分和间接部分(两个部分)。这意味着 Go 中的数组是固定长度的数据结构,由用于存储元素的连续内存块组成。切片是动态大小的,并且引用底层数组的连续段。
为了正确理解这一点,让我们看一下这个数组和切片示例的值部分。



var arr = [5]int{1,2,3,4,5}
var slice = []int{1,2,3,4,5}


Enter fullscreen mode Exit fullscreen mode

数组 ' ' 切片
Go 中数组的内存表示



Go 中数组的内存表示

从上图可以看出,数组是相同类型元素的固定大小集合,存储在连续的内存位置。而切片则由指向底层数组的指针、长度和容量组成。

切片直接部分的内部结构



type _slice struct {
    // referencing underlying elements
    elements unsafe.Pointer
    // number of elements
    len int
    // capacity of the slice
        cap int
}


Enter fullscreen mode Exit fullscreen mode

复制数组和切片时会发生什么

在 Go 中,赋值时不会复制底层值部分,只会复制直接值。这意味着,当我们复制数组时,我们复制的是它的元素(因为它只有直接值部分);但是当我们复制切片时,我们复制的是它的直接值部分len,即 、cappointer to elements间接值部分(实际元素不会被复制)。

数组复制示例



x := [5]int{3, 6, 9, 12, 15}
y := v


Enter fullscreen mode Exit fullscreen mode

在上面的代码示例中,我们初始化了一个数组x,然后创建了另一个变量y,并将的值分配(复制)xy

这是发生的情况的图表表示

数组复制创建新数组
当我们复制一个数组时,所有元素都会被复制到一个单独的内存块中。在上面的代码示例中,如果我们对x数组进行修改,它不会受到影响y,反之亦然,我们稍后会详细讨论这一点。

切片复制示例



x := []int{2,4,6,8,10}
y := x


Enter fullscreen mode Exit fullscreen mode

在上面的代码示例中,我们初始化了一个切片x,然后创建了另一个切片y,并将的值分配(复制)xy

切片的内存表示

从上图可以看出,的直接部分(Len、Cap 和 Elem 指针)x被复制到单独的内存中,但仍然共享相同的底层部分(数组元素)。因此,当你修改 的元素时,由于共享相同的底层元素,因此可以具有不同的长度和容量,因为它们位于各自独立的内存中( 稍后会详细介绍)。yxyxyxy

操作数组和切片

在本节中,我们将讨论操作数组和切片。

数组

由于 Go 中的数组具有固定的常量长度,因此可以对数组执行的唯一操作是从零开始更改特定索引处的值。
第一个元素是0
第二个元素是1
第三个元素,2依此类推。
示例



package main

import "fmt"

func main() {
    var fruits [6]string // Declare string array with zero values
    fmt.Println(fruits)  // => [       ] (string zero value "")

    // 🍊 change first element
    fruits[0] = "Orange"
    fmt.Println(fruits) // => [Orange      ]

    //🍋 change last element
    fruits[5] = "Lemon"
    fmt.Println(fruits) // => [Orange     Lemon]

    // 🍌🍉🍐🍏change all
    fruits[1] = "Banana"
    fruits[2] = "Watermelon"
    fruits[3] = "Pear"
    fruits[4] = "Apple"
    fmt.Println(fruits) // => [Orange Banana Watermelon Pear Apple Lemon]

    //🍍 Dont' like oranges? change first element again
    fruits[0] = "Pineapple"
    fmt.Println(fruits) // => [Pineapple Banana Watermelon Pear Apple Lemon]

    //Modify array of integers
    evenNumbers := [5]int{2, 4, 6, 8, 10}

    evenNumbers[0] = 12
    fmt.Println(evenNumbers) // => [12 4 6 8 10]

    evenNumbers[3] = 20
    fmt.Println(evenNumbers) // => [12 4 6 20 10]

}


Enter fullscreen mode Exit fullscreen mode

访问数组值



import "fmt"

func main() {
    nums := [7]int{1, 2, 3, 4, 5, 6, 7}

    // get the first element
    first := nums[0]
    fmt.Println(first) // => 1

    // get third element
    fmt.Println(nums[2]) // => 3

    // last
    fmt.Println(nums[6]) // => 7

    //alternatively
    fmt.Println(nums[len(nums)-1]) // => 7

}


Enter fullscreen mode Exit fullscreen mode

如果我们尝试将元素的索引更改为大于或等于数组长度,则代码将无法编译,并会panic出现索引超出范围的错误



package main

import "fmt"

func main() {
    nums := [7]int{1, 2, 3, 4, 5, 6, 7}
    outOfBound := nums[7]
}


Enter fullscreen mode Exit fullscreen mode


invalid argument: array index 7 out of bounds [0:6]


Enter fullscreen mode Exit fullscreen mode

切片

切片是 Go 中一种非常有用的数据类型,因为它提供了一种灵活便捷的方式来操作数据集合。切片可以像数组一样访问和修改,但它还具有一些使其功能更强大的特殊行为。在接下来的章节中,我们将更详细地探讨其中一些行为。

切片表达式

切片表达式签名为 ,s[start:end:cap]
用于创建一个包含原始切片所有元素的新切片s,从 index 处的元素开始start,到 index 处的元素(但不包括)为止endcap是新创建的子切片的容量,它是可选的。如果cap省略 ,则子切片的容量等于其长度。子切片的长度为end-start

例子



package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5, 6}
    subSlice := slice[1:4]
    fmt.Println(subSlice)                     // => [2 3 4]
    fmt.Println(len(subSlice), cap(subSlice)) // => 3 3

    subSliceWithCap := slice[1:4:5]
    fmt.Println(subSliceWithCap)                            // => [2 3 4]
    fmt.Println(len(subSliceWithCap), cap(subSliceWithCap)) // => 3 4
}


Enter fullscreen mode Exit fullscreen mode

注意容量也从零开始,因此如果使用5切片的容量s调用cap(s)将返回4

如果start索引为零,则可以省略它;s[:end]同样,如果end索引是数组的末尾,则可以省略它,如下所示s[start:]
示例



package main

import "fmt"

func main() {
    s := []string{"g", "o", " ", "i", "s", " ", "s", "w", "e", "e", "t"}

    // copy from 0 to index 2 (index 2 is exclusive)
    goSubSlice := s[:2]
    fmt.Println(goSubSlice) // => [g o]

    // copy from index 3 to end
    isSweetSubSlice := s[3:]
    fmt.Println(isSweetSubSlice) // => [i s   s w e e t]

        // copy all items
        copySlice := s[:]
        fmt.Println(copySlice) // => [g o  i s   s w e e t]
}


Enter fullscreen mode Exit fullscreen mode

之前我们讨论了切片值的部分以及如何仅复制切片的直接部分,那么当我们创建子切片时实际发生了什么?
让我们使用这个例子



package main

import "fmt"

func main() {
    n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    n1 := n[:6]
    n2 := n[3:8]
    n3 := n[4:10]

    fmt.Println(n1, len(n1), cap(n1)) // => [1 2 3 4 5 6]  6 10
    fmt.Println(n2, len(n2), cap(n2)) // => [4 5 6 7 8]  5 7
    fmt.Println(n3, len(n3), cap(n3)) // => [5 6 7 8 9 10] 6 6

    // change n1 at index 4 to 15
    n1[4] = 15

    fmt.Println(n)  // => [1 2 3 4 15 6 7 8 9 10]
    fmt.Println(n1) // => [1 2 3 4 15 6]
    fmt.Println(n2) // => [4 15 6 7 8]
    fmt.Println(n3) // => [15 6 7 8 9 10]

}


Enter fullscreen mode Exit fullscreen mode

注意,当我们将 更改n1[4]为 15 时,它会影响包括主切片在内的所有其他子切片。这是因为它们共享相同的底层数组元素,因此每当我们对一个子切片进行更改时,它都会影响所有其他子切片。
以下是上述切片和子切片的图表,可以帮助您理解正在发生的事情。

切片共享相同的底层数组,但覆盖不同的部分

从上图我们可以看到,所有子切片共享同一个底层数组,但是它们跨越底层数组的不同部分。当我们在子切片的某个索引处进行更改时,跨度为该索引的所有子切片也会被修改。
当您修改 的索引 4 处的元素时n3,您也会修改数组索引 8 处的元素(n),因为n3和 n 共享同一个底层数组。因此,对 的更改n3[4]也会反映在 中n。同样,对索引 4 到 9(含)之间的 元素所做的任何更改n也将反映在 中n3,因为n3跨越了这些索引。



n3[4] = 18
fmt.Println(n)  // => [1 2 3 4 15 6 7 8 18 10]
fmt.Println(n1) // => [1 2 3 4 15 6]
fmt.Println(n2) // => [4 15 6 7 8]
fmt.Println(n3) // => [15 6 7 8 18 10]


Enter fullscreen mode Exit fullscreen mode
将项目附加到切片

Go 的 append 函数允许你将元素添加到切片的末尾。它的语法如下:



func append(s []T, x ...T) []T


Enter fullscreen mode Exit fullscreen mode

s 是要附加到的切片,x是要附加的一个或多个类型元素的列表T,该函数返回一个带有附加元素的新切片。

例如:



s := []int{1, 2, 3}
s = append(s, 4, 5, 6)
fmt.Println(s) // => s is now [1, 2, 3, 4, 5, 6]


Enter fullscreen mode Exit fullscreen mode

请注意,如果底层数组的容量不足以容纳新增元素,append 将分配一个更大的新数组来保存结果。如果在 append 之后创建了一个更大的新数组,则该切片将不再与其他子切片共享相同的底层数组。
我们以前面的示例为例



package main

import "fmt"

func main() {
    n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    n1 := n[:6]
    n2 := n[3:8]
    n3 := n[4:10]

    fmt.Println(n1, len(n1), cap(n1)) // => [1 2 3 4 5 6]  6 10
    fmt.Println(n2, len(n2), cap(n2)) // => [4 5 6 7 8]  5 7
    fmt.Println(n3, len(n3), cap(n3)) // => [5 6 7 8 9 10] 6 6

}


Enter fullscreen mode Exit fullscreen mode

从上面的代码可以n2看出,它的长度为 5,容量为 7,这意味着我们可以在不创建新数组的情况下添加两个项目,并且它将继续与其他子切片共享相同的底层数组



n2 = append(n2, 100)
fmt.Println(n)  // => [1 2 3 4 15 6 7 8 100 10]
fmt.Println(n1) // => [1 2 3 4 15 6]
fmt.Println(n2) // => [4 15 6 7 8, 100]
fmt.Println(n3) // => [15 6 7 8 100 10]

n2 = append(n2, 101)
fmt.Println(n)  // => [1 2 3 4 15 6 7 8 100 101]
fmt.Println(n1) // => [1 2 3 4 15 6]
fmt.Println(n2) // => [4 15 6 7 8 100 101]
fmt.Println(n3) // => [15 6 7 8 100 101]

// Check the capacity and length of n2
fmt.Println(cap(n2), len(n2)) // =>  7 7


Enter fullscreen mode Exit fullscreen mode

随着我们添加更多元素,它会产生影响nn3但现在n2切片已满(容量 == 长度)。
现在 n2 的容量等于其长度,因此添加新元素将导致为 n2 创建新数组,并且它将不再与其他子切片共享相同的底层数组。



n2 = append(n2, 102)
fmt.Println(n)  // => [1 2 3 4 15 6 7 8 100 101]
fmt.Println(n1) // => [1 2 3 4 15 6]
fmt.Println(n2) // => [4 15 6 7 8 100 101 102]
fmt.Println(n3) // => [15 6 7 8 100 101]


Enter fullscreen mode Exit fullscreen mode

从上面的代码中您可以看到只有n2被修改了,其他的没有受到影响。

将多个项目附加到切片

例子



package main

import "fmt"

func main() {
    s := []int{10, 20, 30, 40, 50, 60}
    s2 := []int{70, 80, 90}

        // Appending slice to slice
    s = append(s, s2...)
    fmt.Println(s) // => [10 20 30 40 50 60 70 80 90]

        // Appending multiple values
    s = append(s, 100, 110, 120)
    fmt.Println(s) // => [10 20 30 40 50 60 70 80 90 100 110 120]
}


Enter fullscreen mode Exit fullscreen mode
切片的深度值复制

深度复制只是意味着复制切片的底层数组而不是直接部分,因此目标切片不会与源切片共享相同的底层内存。
使用追加进行深度复制



package main

import (
    "fmt"
)

func main() {
    slice1 := []int{1, 2, 3, 4, 5, 6}
    slice2 := []int{}
    slice2 = append(slice2, slice1...)

    fmt.Println(slice1) // => [1 2 3 4 5 6]
    fmt.Println(slice2) // => [1 2 3 4 5 6]

    //Modifying slice2 doesn't affect slice1
    slice2[0] = 100
    fmt.Println(slice1) // => [1 2 3 4 5 6]
    fmt.Println(slice2) // => [100 2 3 4 5 6]

    // copying a range of items
    slice3 := []int{}
    slice3 = append(slice3, slice1[3:5]...)
    fmt.Println(slice3) // => [4 5]

    //Again slice3 and slice1 doesn't share underlying array

    slice3[0] = -10
    fmt.Println(slice1) // => [1 2 3 4 5 6]
    fmt.Println(slice3) // => [-10 5]

}


Enter fullscreen mode Exit fullscreen mode

在 Go 中,可以使用 copy 函数对切片进行深层复制。深层复制会创建一个新的切片,其中包含原始切片元素的独立副本。

复制函数的语法如下:



func copy(dst, src []T) int


Enter fullscreen mode Exit fullscreen mode

dst是目标切片,src是源切片。两个切片必须具有相同的元素类型T。该函数返回复制的元素数量,该数量将是dst和 的长度中的最小值src

例如:



package main

import "fmt"

func main() {
    s := []int{1, 2, 3}
    t := make([]int, len(s))
    copy(t, s)
    fmt.Println(t) // => [1, 2, 3], and is a deep copy of s

    // copy a range of items

    t = make([]int, len(s)-1)
    copy(t, s[0:2])
    fmt.Println(t) // => [1, 2], and is a deep copy of s

}


Enter fullscreen mode Exit fullscreen mode

请注意,如果 的长度dst小于 的长度src,则只会复制 的第一个len(dst)元素src。要对整个切片进行深层复制,必须确保dst有足够的容量来容纳 的所有元素src

切片的潜在怪癖

如前所述,子切片仅复制切片的直接部分并共享相同的底层数组。因此,当我们有一个sBig大小为 10 MB 的切片时,我们会sTiny从 创建一个大小为 3 个字节的子切片sBigsTiny并且sBig将引用相同的底层数组。您可能知道 Go 是垃圾收集的,这意味着当它不被引用(无法访问)时它会自动释放内存。所以在
的情况下,即使我们只需要3 个字节,它仍将继续在内存中,因为它引用了与 相同的底层数组。为了解决这个问题,我们进行了深度复制,以便不与 共享底层数组,因此它可以被垃圾收集从而释放内存。 例子sBigsTinysTinysBigsTinysBigsTinysBig



var gopherRegexp = regexp.MustCompile("gopher")

func FindGopher(filename string) []byte {
    //Reading a very huge file  1,000,000,000 bytes (1GB)
    b, _ := ioutil.ReadFile(filename)
    //Taking a just 6 byte sub slice
    gopherSlice := gopherRegexp.Find(b)
    return gopherSlice
}


Enter fullscreen mode Exit fullscreen mode

从上面的例子中,我们读取了一个非常大的文件(1GB),并返回了它的一个子切片(仅 6 个字节),因为它gopherSlice仍然引用与这个大文件相同的底层数组,这意味着即使我们不再使用这 1GB 的内存,也无法被垃圾回收。
如果多次调用该FindGopher函数,你的程序可能会耗尽计算机的所有内存。为了解决这个问题,就像我之前说的,我们进行了一次深层复制,这样gopherSlice就不会与这个大切片共享相同的底层数组。
示例



var gopherRegexp = regexp.MustCompile("gopher")

func FindGopher(filename string) []byte {
    //Reading a very huge file  1,000,000,000 bytes (1GB)
    b, _ := ioutil.ReadFile(filename)
    //Taking a just 6 byte sub slice
    gopherSlice := make([]byte, len("gopher"))

    // Make a deep copy
    copy(gopherSlice, gopherRegexp.Find(b...)
    return gopherSlice
}


Enter fullscreen mode Exit fullscreen mode

现在 Go 语言垃圾收集器可以释放约 1GB 的内存

优化代码性能的技巧

就像我之前说的,Go 中数组和切片之间最重要的区别是它们的值部分的不同,这与 Go 值复制成本一起是它们性能不同的原因。

价值复制成本

赋值、参数传递、使用range关键字循环等都涉及值复制。值越大,复制成本就越高,复制 10 MB 比复制 10 字节要耗时更长。我们还了解到,只有直接部分会被复制。
示例



array := [100]int{1,2,3,4,5,6, ..., 100}
slice := []int{1,2,3,4,5,6, ..., 100}


Enter fullscreen mode Exit fullscreen mode

从上面的例子中,我们创建了一个数字数组1-100和一个同样包含数字的切片1-100。复制数组时,所有元素都会被复制,因此值的复制成本为(假设在 64 位架构下为8 * 100 = 800 bytes1 )。但是复制切片时,仅复制直接部分( ),因此值的复制成本为。即使切片和数组都包含 100 个元素,数组的值复制成本也远高于切片。int8 byteslencapelements pointer8 + 8 + 8 = 24 bytes

从上述场景来看,性能问题仅影响数组而非切片,我将重点讨论如何在使用数组时兼顾性能。此外,对于较小的数组,这些性能差异可以忽略不计,不值得为了提高速度而付出努力。

这里唯一的事情是不使用range关键字来循环数组



package main

import "fmt"

func main() {
// Don't do this
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

<span class="c">// arr is copied</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">arr</span> <span class="p">{</span>
    <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="p">}</span>

<span class="c">// Do this instead</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">arr</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
    <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">arr</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="p">}</span>
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode




结论

如果您有任何疑问,请在评论中提出,我很乐意回答;如果您希望我
写点什么,请在评论中写下。

鏂囩珷鏉ユ簮锛�https://dev.to/dawkaka/go-arrays-and-slices-a-deep-dive-dp8
PREV
使用 Next-Auth 和 MongoDB 进行凭据身份验证 - 第 1 部分
NEXT
从 Apache2 迁移到 Nginx Web 服务器