赞
踩
在Go中,数组和切片是由有序的元素序列组成的数据结构。当需要处理许多相关值时,这些数据集非常适合使用。它们使你能够将本应放在一起的数据放在一起,压缩代码,并一次性对多个值执行相同的方法和操作。
尽管Go中的数组和切片都是有序的元素序列,但两者之间存在显著差异。Go中的数组是一种数据结构,由在创建时定义其容量的有序元素序列组成。一旦数组分配了大小,就不能再改变它的大小。另一方面,切片是数组的变长版本,为使用这些数据结构的开发人员提供了更大的灵活性。切片构成了其他语言中的数组。
考虑到这些差异,在某些特定情况下,您将使用其中一个。如果您是Go的新手,确定何时使用它们可能会令人困惑:尽管切片的多功能性使它们在大多数情况下是更合适的选择,但在某些特定情况下,数组可以优化程序的性能。
本文将详细介绍数组和切片,为您在选择这些数据类型时做出适当的选择提供必要的信息。此外,您还将回顾声明和使用数组和切片的最常见方法。本教程将首先描述数组以及如何操作它们,然后解释切片及其区别。
数组是包含一定数量元素的集合数据结构。因为数组的大小是静态的,所以数据结构只需要分配一次内存,而变长数据结构必须动态分配内存,以便将来内存变得更大或更小。虽然数组的固定长度会让它们在使用时有些死板,但一次性分配内存可以提高程序的速度和性能。因此,开发人员在优化程序时,如果数据结构永远不需要可变数量的元素,通常会使用数组。
数组是通过在方括号’[]'中声明数组的大小和元素的数据类型来定义的。Go中的数组必须具有相同的[数据类型]在数据类型之后,您可以在花括号{}
中声明数组元素的单个值。
下面是声明数组的一般模式:
[capacity]data_type{element_values}
**注意:**重要的是要记住,每个新数组的声明都会创建一个不同的类型。因此,尽管[2]int
和[3]int
都有整数元素,但它们不同的长度使它们的数据类型不兼容。
如果不声明数组元素的值,则默认值为0,这意味着数组中的元素将为空。对于整数,this用0
表示,对于字符串,this用空字符串表示。
例如,下面的数组numbers
有三个还没有值的整数元素:
var numbers [3]int
如果你打印numbers
,你会得到以下输出:
Output[0 0 0]
如果想在创建数组时对元素的值进行赋值,可以将值放在大括号中。一个设置了值的字符串数组如下所示:
[4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
你可以将数组存储在变量中,并将其打印出来:
coral := [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
fmt.Println(coral)
Output[blue coral staghorn coral pillar coral elkhorn coral]
请注意,在打印数组时,没有对数组中的元素进行划分,因此很难区分一个元素从哪里结束,另一个元素从哪里开始。因此,有时使用fmt.Printf
函数是有帮助的,它可以在将字符串打印到屏幕上之前格式化字符串。为动词%q
提供这个命令,指示函数在值周围加上引号:
fmt.Printf("%q\n", coral)
Output["blue coral" "staghorn coral" "pillar coral" "elkhorn coral"]
现在每个项目都被引用了。\n
动词指示格式化程序在末尾添加一行返回。
有了如何声明数组及其组成的一般概念,你现在可以继续学习如何用索引号指定数组中的元素。
数组(以及切片)中的每个元素都可以通过索引来单独调用。每个元素对应一个索引数,它是一个从索引数0
开始的int
值。
在下面的例子中,我们将使用数组,但你也可以使用切片,因为它们的索引方式相同。
对于数组coral
,索引分解如下所示:
“blue coral” | “staghorn coral” | “pillar coral” | “elkhorn coral” |
---|---|---|---|
0 | 1 | 2 | 3 |
第一个元素是字符串"blue coral"
,从索引0
开始,切片以索引3
结束,元素是"elkhorn coral"
。
因为切片或数组中的每个元素都有对应的索引号,所以我们能够以与其他顺序数据类型相同的方式访问和操作它们。
现在我们可以通过索引号来调用切片中的离散元素:
fmt.Println(coral[1])
Outputstaghorn coral
这个切片的索引号从0-3
,如前一个表所示。因此,要单独调用任何元素,我们将像这样引用索引编号:
coral[0] = "blue coral"
coral[1] = "staghorn coral"
coral[2] = "pillar coral"
coral[3] = "elkhorn coral"
如果我们使用任何大于3
的索引值来调用数组coral
,它将超出范围,因为它将无效:
fmt.Println(coral[18])
Outputpanic: runtime error: index out of range
当索引数组或切片时,必须始终使用正数。与某些语言允许您使用负数反向索引不同,在Go中这样做将导致错误:
fmt.Println(coral[-1])
Outputinvalid array index -1 (index must be non-negative)
我们可以使用+
操作符连接数组中的字符串元素或与其他字符串切片:
fmt.Println("Sammy loves " + coral[0])
OutputSammy loves blue coral
我们可以将索引为0
的字符串元素与字符串"Sammy loves "
连接起来。
通过索引号对应数组或切片中的元素,我们能够离散地访问每个元素并使用这些元素。为了演示这一点,我们接下来看看如何修改某个索引处的元素。
我们可以通过将索引编号的元素设置为不同的值,来使用索引来更改数组或切片中的元素。这使我们能够更好地控制切片和数组中的数据,并允许我们以编程方式操作单个元素。
如果我们想要将数组coral
中索引1
处的元素的字符串值从"staghorn coral"
改为"foliose coral"
,我们可以这样做:
coral[1] = "foliose coral"
现在当我们打印coral
时,数组将会不同:
fmt.Printf("%q\n", coral)
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral"]
现在你已经知道了如何操作数组或切片中的单个元素,下面来看两个函数,它们能让你在处理集合数据类型时更加灵活。
len()
计数元素在Go中,len()
是一个内置函数,用于帮助你处理数组和切片。与字符串一样,你可以使用len()
并将数组或切片作为参数传递来计算数组或切片的长度。
例如,要找出coral
数组中有多少个元素,你可以使用:
len(coral)
如果你打印数组 coral
的长度,会得到以下输出:
Output4
这给出了数组4
的int
数据类型的长度,这是正确的,因为数组coral
有四个元素:
coral := [4]string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral"}
如果你创建一个包含更多元素的整数数组,你也可以对其使用len()
函数:
numbers := [13]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
fmt.Println(len(numbers))
Output13
尽管这些示例数组的项相对较少,但在确定非常大的数组中有多少元素时,len()
函数特别有用。
接下来,我们将介绍如何将元素添加到集合数据类型中,并演示由于数组的长度是固定的,添加这些静态数据类型将导致错误。
append()
添加元素append()
是Go中的一个内置方法,它可以将元素添加到集合数据类型中。但是,这个方法不适用于数组。如前所述,数组与切片的主要区别在于数组的大小不能修改。这意味着,虽然可以改变数组中元素的值,但不能在定义数组后使其变大或变小。
让我们考虑一下你的coral
数组:
coral := [4]string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral"}
假设你想将元素"black coral"
添加到这个数组中。如果你尝试对数组使用append()
函数,输入:
coral = append(coral, "black coral")
Outputfirst argument to append must be slice; have [4]string
为了解决这个问题,让我们学习更多关于切片数据类型的内容,如何定义切片,以及如何将数组转换为切片。
切片是Go中的一种数据类型,它是一个可变的或可变的、有序的元素序列。由于切片的大小是可变的,因此在使用它们时具有更大的灵活性;在处理将来可能需要扩展或收缩的数据集合时,使用切片可以确保在试图操作集合的长度时不会出错。在大多数情况下,这种可变性是值得的,与数组相比,切片有时需要重新分配内存。当你需要存储大量元素或迭代元素,并希望能够随时修改这些元素时,你可能会想使用slice数据类型。
切片是通过声明数据类型来定义的,数据类型之前是一组空的方括号([]
),以及大括号({}
)之间的元素列表。你会注意到,与数组需要在括号中包含一个int
来声明特定的长度不同,切片在括号中没有任何东西来表示它的可变长度。
让我们创建一个包含string数据类型元素的切片:
seaCreatures := []string{"shark", "cuttlefish", "squid", "mantis shrimp", "anemone"}
打印切片时,我们可以看到切片中的元素:
fmt.Printf("%q\n", seaCreatures)
Output["shark" "cuttlefish" "squid" "mantis shrimp" "anemone"]
如果你想创建一个特定长度的切片而不填充集合中的元素,你可以使用内置的make()
函数:
oceans := make([]string, 3)
Output["" "" ""]
如果你想预分配一定容量的内存,你可以向make()
传入第三个参数:
oceans := make([]string, 3, 5)
这将创建一个长度为3
、预分配容量为5
的零切片。
现在你知道如何声明切片了。然而,这还不能解决我们之前在coral
数组中遇到的错误。要对coral
使用append()
函数,你首先需要学习如何对数组进行切片。
通过使用索引号来确定起点和终点,可以调用数组中值的分段。这被称为切片数组,你可以通过创建一个以冒号分隔的索引数字范围来做到这一点,形式为[==first_index==:==second_index==]
。然而,重要的是要注意,在对数组进行切片时,结果是切片,而不是数组。
假设你想只打印coral
数组的中间元素,不打印第一个和最后一个元素。你可以通过创建一个从索引1
开始,在索引3
之前结束的切片来实现:
fmt.Println(coral[1:3])
Output[foliose coral pillar coral]
在创建切片时,如[1:3]
,第一个数字是切片的起始位置(包括起始位置),第二个数字是第一个数字和你想要检索的元素总数的和:
array[starting_index : (starting_index + length_of_slice)]
在本例中,调用第二个元素(或索引1)作为起点,并总共调用了两个元素。计算如下:
array[1 : (1 + 2)]
这就是你如何得到这个符号的:
coral[1:3]
如果想将数组的开始或结束设置为切片的开始或结束点,可以省略array[==first_index==:==second_index==]
语法中的一个数字。例如,如果你想打印数组coral
的前三项——即"blue coral"
、"foliose coral"
和"pillar coral"
——你可以输入:
fmt.Println(coral[:3])
Output[blue coral foliose coral pillar coral]
这打印了数组的开始部分,在索引3
之前停止。
要在数组末尾包含所有元素,可以使用相反的语法:
fmt.Println(coral[1:])
Output[foliose coral pillar coral elkhorn coral]
本节讨论了如何通过切片调用数组的各个部分。接下来,你将学习如何使用切片将整个数组转换为切片。
如果你创建了一个数组,并决定让它具有可变长度,则可以将其转换为切片。要将数组转换为切片,请使用本教程的slicing Arrays into Slices步骤中学习的切片过程,只不过这次通过省略两个决定端点的索引号来选择整个切片:
coral[:]
请记住,你不能将变量coral
转换为切片本身,因为变量一旦在Go中定义,其类型就不能更改。要解决这个问题,可以将数组的整个内容以切片的形式复制到一个新变量中:
coralSlice := coral[:]
如果你打印coralSlice
,你会收到以下输出:
Output[blue coral foliose coral pillar coral elkhorn coral]
现在,尝试像数组部分那样添加black coral
元素,对新转换后的切片使用append()
:
coralSlice = append(coralSlice, "black coral")
fmt.Printf("%q\n", coralSlice)
这将输出添加了元素的切片:
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral"]
我们也可以在一个append()
语句中添加多个元素:
coralSlice = append(coralSlice, "antipathes", "leptopsammia")
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia"]
要将两个切片组合在一起,你可以使用append()
,但你必须扩展第二个参数以使用…
扩展语法:
moreCoral := []string{"massive coral", "soft coral"}
coralSlice = append(coralSlice, moreCoral...)
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]
现在你已经学习了如何将元素添加到切片中,我们来看看如何删除一个元素。
与其他语言不同,Go不提供任何从切片中删除元素的内置函数。需要通过切片的方式将项目从切片中移除。
要删除一个元素,必须切出该元素之前的元素,切出该元素之后的元素,然后将这两个新切片附加在一起,而不包含要删除的元素。
如果i
是待删除元素的索引,则此过程的格式如下所示:
slice = append(slice[:i], slice[i+1:]...)
从coralSlice
中,移除"elkhorn coral"
元素。这个元素位于3
的索引位置。
coralSlice := []string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral", "black coral", "antipathes", "leptopsammia", "massive coral", "soft coral"}
coralSlice = append(coralSlice[:3], coralSlice[4:]...)
fmt.Printf("%q\n", coralSlice)
Output["blue coral" "foliose coral" "pillar coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]
现在,索引位置3
处的元素,即字符串"elkhorn coral"
,不再位于我们的切片coralSlice
中。
我们也可以使用相同的方法删除范围。假设我们不仅要删除元素"elkhorn coral"
,还要删除"black coral"
和"antipathes"
。我们可以在表达式中使用范围来实现这一点:
coralSlice := []string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral", "black coral", "antipathes", "leptopsammia", "massive coral", "soft coral"}
coralSlice = append(coralSlice[:3], coralSlice[6:]...)
fmt.Printf("%q\n", coralSlice)
这段代码将从切片中取出索引3
、4
和5
:
Output["blue coral" "foliose coral" "pillar coral" "leptopsammia" "massive coral" "soft coral"]
现在你已经知道了如何在切片中添加和删除元素,让我们看看如何测量在任何给定时间切片可以容纳的数据量。
cap()
测量切片的容量由于切片的长度是可变的,因此len()
方法不是确定这种数据类型大小的最佳选择。相反,你可以使用cap()
函数来了解切片的容量。这将显示一个切片可以容纳多少个元素,这是由该切片已经分配了多少内存决定的。
**注意:**因为数组的长度和容量总是相同的,所以cap()
函数对数组不起作用。
cap()
的一个常见用途是使用预设的元素数量创建一个切片,然后以编程方式填充这些元素。这避免了使用append()
添加超出当前分配容量的元素时可能发生的潜在不必要的分配。
假设我们想创建一个数字列表,0
到3
。我们可以在循环中使用append()
来做到这一点,或者我们可以先预分配切片,然后使用cap()
循环来填充值。
首先,我们来看看如何使用append()
:
numbers := []int{}
for i := 0; i < 4; i++ {
numbers = append(numbers, i)
}
fmt.Println(numbers)
Output[0 1 2 3]
在这个例子中,我们创建了一个切片,然后创建了一个for
循环,它将迭代四次。每次迭代都会将循环变量i
的当前值添加到numbers
切片的下标中。然而,这可能会导致不必要的内存分配,从而降低程序的速度。在给空切片添加容量时,每次调用append时,程序都会检查切片的容量。如果添加的元素使切片超过这个容量,程序将分配额外的内存来计算它。这会给程序带来额外的开销,并导致执行速度变慢。
现在让我们通过预先分配一定的长度/容量来在不使用append()
的情况下填充切片:
numbers := make([]int, 4)
for i := 0; i < cap(numbers); i++ {
numbers[i] = i
}
fmt.Println(numbers)
Output[0 1 2 3]
在这个例子中,我们使用make()
来创建一个切片,并让它预分配4
个元素。然后,我们在循环中使用cap()
函数迭代每个置零的元素,填充每个元素,直到达到预分配的容量。在每个循环中,我们将循环变量i
的当前值放置到numbers
切片的下标中。
虽然append()
和cap()
策略在功能上是等效的,但cap()
的例子避免了使用append()
函数所需的任何额外内存分配。
您还可以将由其他切片组成的切片定义为元素,每个方括号中的列表都包含在父切片的大括号中。这样的切片集合称为多维切片。这些可以被认为是描绘多维坐标;例如,一个由5个长为6个元素的切片组成的集合可以表示一个水平长度为5、垂直高度为6的二维网格。
让我们来看看下面的多维切片:
seaNames := [][]string{{"shark", "octopus", "squid", "mantis shrimp"}, {"Sammy", "Jesse", "Drew", "Jamie"}}
为了访问切片中的元素,我们需要使用多个索引,每个维度对应一个索引:
fmt.Println(seaNames[1][0])
fmt.Println(seaNames[0][0])
在上面的代码中,我们首先标识索引1
处切片的索引0
处的元素,然后标识索引0
处切片的索引0
处的元素。这将产生以下结果:
OutputSammy
shark
下面是其余各个元素的索引值:
seaNames[0][0] = "shark"
seaNames[0][1] = "octopus"
seaNames[0][2] = "squid"
seaNames[0][3] = "mantis shrimp"
seaNames[1][0] = "Sammy"
seaNames[1][1] = "Jesse"
seaNames[1][2] = "Drew"
seaNames[1][3] = "Jamie"
在处理多维切片时,一定要记住,为了访问相关嵌套切片中的特定元素,需要引用多个索引号。
在本教程中,您学习了在Go中使用数组和切片的基础知识。我们通过多个练习演示了数组的长度是固定的,而切片的长度是可变的,并发现了这种差异如何影响在不同情况下对这些数据结构的使用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。