当前位置:   article > 正文

理解Go中的数组(Array)和切片(Slice)_go slice是有序的吗

go slice是有序的吗

引言

在Go中,数组切片是由有序的元素序列组成的数据结构。当需要处理许多相关值时,这些数据集非常适合使用。它们使你能够将本应放在一起的数据放在一起,压缩代码,并一次性对多个值执行相同的方法和操作。

尽管Go中的数组和切片都是有序的元素序列,但两者之间存在显著差异。Go中的数组是一种数据结构,由在创建时定义其容量的有序元素序列组成。一旦数组分配了大小,就不能再改变它的大小。另一方面,切片是数组的变长版本,为使用这些数据结构的开发人员提供了更大的灵活性。切片构成了其他语言中的数组。

考虑到这些差异,在某些特定情况下,您将使用其中一个。如果您是Go的新手,确定何时使用它们可能会令人困惑:尽管切片的多功能性使它们在大多数情况下是更合适的选择,但在某些特定情况下,数组可以优化程序的性能。

本文将详细介绍数组和切片,为您在选择这些数据类型时做出适当的选择提供必要的信息。此外,您还将回顾声明和使用数组和切片的最常见方法。本教程将首先描述数组以及如何操作它们,然后解释切片及其区别。

数组

数组是包含一定数量元素的集合数据结构。因为数组的大小是静态的,所以数据结构只需要分配一次内存,而变长数据结构必须动态分配内存,以便将来内存变得更大或更小。虽然数组的固定长度会让它们在使用时有些死板,但一次性分配内存可以提高程序的速度和性能。因此,开发人员在优化程序时,如果数据结构永远不需要可变数量的元素,通常会使用数组。

定义数组

数组是通过在方括号’[]'中声明数组的大小和元素的数据类型来定义的。Go中的数组必须具有相同的[数据类型]在数据类型之后,您可以在花括号{}中声明数组元素的单个值。

下面是声明数组的一般模式:

[capacity]data_type{element_values}
  • 1

**注意:**重要的是要记住,每个新数组的声明都会创建一个不同的类型。因此,尽管[2]int[3]int都有整数元素,但它们不同的长度使它们的数据类型不兼容。

如果不声明数组元素的值,则默认值为0,这意味着数组中的元素将为空。对于整数,this用0表示,对于字符串,this用空字符串表示。

例如,下面的数组numbers有三个还没有值的整数元素:

var numbers [3]int
  • 1

如果你打印numbers,你会得到以下输出:

Output[0 0 0]
  • 1

如果想在创建数组时对元素的值进行赋值,可以将值放在大括号中。一个设置了值的字符串数组如下所示:

[4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
  • 1

你可以将数组存储在变量中,并将其打印出来:

coral := [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
fmt.Println(coral)
  • 1
  • 2
Output[blue coral staghorn coral pillar coral elkhorn coral]
  • 1

请注意,在打印数组时,没有对数组中的元素进行划分,因此很难区分一个元素从哪里结束,另一个元素从哪里开始。因此,有时使用fmt.Printf函数是有帮助的,它可以在将字符串打印到屏幕上之前格式化字符串。为动词%q提供这个命令,指示函数在值周围加上引号:

fmt.Printf("%q\n", coral)
  • 1
Output["blue coral" "staghorn coral" "pillar coral" "elkhorn coral"]
  • 1

现在每个项目都被引用了。\n动词指示格式化程序在末尾添加一行返回。

有了如何声明数组及其组成的一般概念,你现在可以继续学习如何用索引号指定数组中的元素。

索引数组(和切片)

数组(以及切片)中的每个元素都可以通过索引来单独调用。每个元素对应一个索引数,它是一个从索引数0开始的int值。

在下面的例子中,我们将使用数组,但你也可以使用切片,因为它们的索引方式相同。

对于数组coral,索引分解如下所示:

“blue coral”“staghorn coral”“pillar coral”“elkhorn coral”
0123

第一个元素是字符串"blue coral",从索引0开始,切片以索引3结束,元素是"elkhorn coral"

因为切片或数组中的每个元素都有对应的索引号,所以我们能够以与其他顺序数据类型相同的方式访问和操作它们。

现在我们可以通过索引号来调用切片中的离散元素:

fmt.Println(coral[1])
  • 1
Outputstaghorn coral
  • 1

这个切片的索引号从0-3,如前一个表所示。因此,要单独调用任何元素,我们将像这样引用索引编号:

coral[0] = "blue coral"
coral[1] = "staghorn coral"
coral[2] = "pillar coral"
coral[3] = "elkhorn coral"
  • 1
  • 2
  • 3
  • 4

如果我们使用任何大于3的索引值来调用数组coral,它将超出范围,因为它将无效:

fmt.Println(coral[18])
  • 1
Outputpanic: runtime error: index out of range
  • 1

当索引数组或切片时,必须始终使用正数。与某些语言允许您使用负数反向索引不同,在Go中这样做将导致错误:

fmt.Println(coral[-1])
  • 1
Outputinvalid array index -1 (index must be non-negative)
  • 1

我们可以使用+操作符连接数组中的字符串元素或与其他字符串切片:

fmt.Println("Sammy loves " + coral[0])
  • 1
OutputSammy loves blue coral
  • 1

我们可以将索引为0的字符串元素与字符串"Sammy loves "连接起来。

通过索引号对应数组或切片中的元素,我们能够离散地访问每个元素并使用这些元素。为了演示这一点,我们接下来看看如何修改某个索引处的元素。

修改元素

我们可以通过将索引编号的元素设置为不同的值,来使用索引来更改数组或切片中的元素。这使我们能够更好地控制切片和数组中的数据,并允许我们以编程方式操作单个元素。

如果我们想要将数组coral中索引1处的元素的字符串值从"staghorn coral"改为"foliose coral",我们可以这样做:

coral[1] = "foliose coral"
  • 1

现在当我们打印coral时,数组将会不同:

fmt.Printf("%q\n", coral)
  • 1
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral"]
  • 1

现在你已经知道了如何操作数组或切片中的单个元素,下面来看两个函数,它们能让你在处理集合数据类型时更加灵活。

使用len()计数元素

在Go中,len()是一个内置函数,用于帮助你处理数组和切片。与字符串一样,你可以使用len()并将数组或切片作为参数传递来计算数组或切片的长度。

例如,要找出coral数组中有多少个元素,你可以使用:

len(coral)
  • 1

如果你打印数组 coral的长度,会得到以下输出:

Output4
  • 1

这给出了数组4int数据类型的长度,这是正确的,因为数组coral有四个元素:

coral := [4]string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral"}
  • 1

如果你创建一个包含更多元素的整数数组,你也可以对其使用len()函数:

numbers := [13]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
fmt.Println(len(numbers))
  • 1
  • 2
Output13
  • 1

尽管这些示例数组的项相对较少,但在确定非常大的数组中有多少元素时,len()函数特别有用。

接下来,我们将介绍如何将元素添加到集合数据类型中,并演示由于数组的长度是固定的,添加这些静态数据类型将导致错误。

append()添加元素

append()是Go中的一个内置方法,它可以将元素添加到集合数据类型中。但是,这个方法不适用于数组。如前所述,数组与切片的主要区别在于数组的大小不能修改。这意味着,虽然可以改变数组中元素的值,但不能在定义数组后使其变大或变小。

让我们考虑一下你的coral数组:

coral := [4]string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral"}
  • 1

假设你想将元素"black coral"添加到这个数组中。如果你尝试对数组使用append()函数,输入:

coral = append(coral, "black coral")
  • 1
Outputfirst argument to append must be slice; have [4]string
  • 1

为了解决这个问题,让我们学习更多关于切片数据类型的内容,如何定义切片,以及如何将数组转换为切片。

切片

切片是Go中的一种数据类型,它是一个可变的或可变的、有序的元素序列。由于切片的大小是可变的,因此在使用它们时具有更大的灵活性;在处理将来可能需要扩展或收缩的数据集合时,使用切片可以确保在试图操作集合的长度时不会出错。在大多数情况下,这种可变性是值得的,与数组相比,切片有时需要重新分配内存。当你需要存储大量元素或迭代元素,并希望能够随时修改这些元素时,你可能会想使用slice数据类型。

定义切片

切片是通过声明数据类型来定义的,数据类型之前是一组空的方括号([]),以及大括号({})之间的元素列表。你会注意到,与数组需要在括号中包含一个int来声明特定的长度不同,切片在括号中没有任何东西来表示它的可变长度。

让我们创建一个包含string数据类型元素的切片:

seaCreatures := []string{"shark", "cuttlefish", "squid", "mantis shrimp", "anemone"}
  • 1

打印切片时,我们可以看到切片中的元素:

fmt.Printf("%q\n", seaCreatures)
  • 1
Output["shark" "cuttlefish" "squid" "mantis shrimp" "anemone"]
  • 1

如果你想创建一个特定长度的切片而不填充集合中的元素,你可以使用内置的make()函数:

oceans := make([]string, 3)
  • 1
Output["" "" ""]
  • 1

如果你想预分配一定容量的内存,你可以向make()传入第三个参数:

oceans := make([]string, 3, 5)
  • 1

这将创建一个长度为3、预分配容量为5的零切片。

现在你知道如何声明切片了。然而,这还不能解决我们之前在coral数组中遇到的错误。要对coral使用append()函数,你首先需要学习如何对数组进行切片。

将数组切片

通过使用索引号来确定起点和终点,可以调用数组中值的分段。这被称为切片数组,你可以通过创建一个以冒号分隔的索引数字范围来做到这一点,形式为[==first_index==:==second_index==]。然而,重要的是要注意,在对数组进行切片时,结果是切片,而不是数组。

假设你想只打印coral数组的中间元素,不打印第一个和最后一个元素。你可以通过创建一个从索引1开始,在索引3之前结束的切片来实现:

fmt.Println(coral[1:3])
  • 1
Output[foliose coral pillar coral]
  • 1

在创建切片时,如[1:3],第一个数字是切片的起始位置(包括起始位置),第二个数字是第一个数字和你想要检索的元素总数的和:

array[starting_index : (starting_index + length_of_slice)]
  • 1

在本例中,调用第二个元素(或索引1)作为起点,并总共调用了两个元素。计算如下:

array[1 : (1 + 2)]
  • 1

这就是你如何得到这个符号的:

coral[1:3]
  • 1

如果想将数组的开始或结束设置为切片的开始或结束点,可以省略array[==first_index==:==second_index==]语法中的一个数字。例如,如果你想打印数组coral的前三项——即"blue coral""foliose coral""pillar coral"——你可以输入:

fmt.Println(coral[:3])
  • 1
Output[blue coral foliose coral pillar coral]
  • 1

这打印了数组的开始部分,在索引3之前停止。

要在数组末尾包含所有元素,可以使用相反的语法:

fmt.Println(coral[1:])
  • 1
Output[foliose coral pillar coral elkhorn coral]
  • 1

本节讨论了如何通过切片调用数组的各个部分。接下来,你将学习如何使用切片将整个数组转换为切片。

将数组转换为切片

如果你创建了一个数组,并决定让它具有可变长度,则可以将其转换为切片。要将数组转换为切片,请使用本教程的slicing Arrays into Slices步骤中学习的切片过程,只不过这次通过省略两个决定端点的索引号来选择整个切片:

coral[:]
  • 1

请记住,你不能将变量coral转换为切片本身,因为变量一旦在Go中定义,其类型就不能更改。要解决这个问题,可以将数组的整个内容以切片的形式复制到一个新变量中:

coralSlice := coral[:]
  • 1

如果你打印coralSlice,你会收到以下输出:

Output[blue coral foliose coral pillar coral elkhorn coral]
  • 1

现在,尝试像数组部分那样添加black coral元素,对新转换后的切片使用append():

coralSlice = append(coralSlice, "black coral")
fmt.Printf("%q\n", coralSlice)
  • 1
  • 2

这将输出添加了元素的切片:

Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral"]
  • 1

我们也可以在一个append()语句中添加多个元素:

coralSlice = append(coralSlice, "antipathes", "leptopsammia")
  • 1
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia"]
  • 1

要将两个切片组合在一起,你可以使用append(),但你必须扩展第二个参数以使用扩展语法:

moreCoral := []string{"massive coral", "soft coral"}
coralSlice = append(coralSlice, moreCoral...)
  • 1
  • 2
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]
  • 1

现在你已经学习了如何将元素添加到切片中,我们来看看如何删除一个元素。

从切片中删除元素

与其他语言不同,Go不提供任何从切片中删除元素的内置函数。需要通过切片的方式将项目从切片中移除。

要删除一个元素,必须切出该元素之前的元素,切出该元素之后的元素,然后将这两个新切片附加在一起,而不包含要删除的元素。

如果i是待删除元素的索引,则此过程的格式如下所示:

slice = append(slice[:i], slice[i+1:]...)
  • 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)
  • 1
  • 2
  • 3
  • 4
  • 5
Output["blue coral" "foliose coral" "pillar coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]
  • 1

现在,索引位置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)
  • 1
  • 2
  • 3
  • 4
  • 5

这段代码将从切片中取出索引345:

Output["blue coral" "foliose coral" "pillar coral" "leptopsammia" "massive coral" "soft coral"]
  • 1

现在你已经知道了如何在切片中添加和删除元素,让我们看看如何测量在任何给定时间切片可以容纳的数据量。

使用cap()测量切片的容量

由于切片的长度是可变的,因此len()方法不是确定这种数据类型大小的最佳选择。相反,你可以使用cap()函数来了解切片的容量。这将显示一个切片可以容纳多少个元素,这是由该切片已经分配了多少内存决定的。

**注意:**因为数组的长度和容量总是相同的,所以cap()函数对数组不起作用。

cap()的一个常见用途是使用预设的元素数量创建一个切片,然后以编程方式填充这些元素。这避免了使用append()添加超出当前分配容量的元素时可能发生的潜在不必要的分配。

假设我们想创建一个数字列表,03。我们可以在循环中使用append()来做到这一点,或者我们可以先预分配切片,然后使用cap()循环来填充值。

首先,我们来看看如何使用append():

numbers := []int{}
for i := 0; i < 4; i++ {
	numbers = append(numbers, i)
}
fmt.Println(numbers)
  • 1
  • 2
  • 3
  • 4
  • 5
Output[0 1 2 3]
  • 1

在这个例子中,我们创建了一个切片,然后创建了一个for循环,它将迭代四次。每次迭代都会将循环变量i的当前值添加到numbers切片的下标中。然而,这可能会导致不必要的内存分配,从而降低程序的速度。在给空切片添加容量时,每次调用append时,程序都会检查切片的容量。如果添加的元素使切片超过这个容量,程序将分配额外的内存来计算它。这会给程序带来额外的开销,并导致执行速度变慢。

现在让我们通过预先分配一定的长度/容量来在不使用append()的情况下填充切片:

numbers := make([]int, 4)
for i := 0; i < cap(numbers); i++ {
	numbers[i] = i
}

fmt.Println(numbers)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
Output[0 1 2 3]
  • 1

在这个例子中,我们使用make()来创建一个切片,并让它预分配4个元素。然后,我们在循环中使用cap()函数迭代每个置零的元素,填充每个元素,直到达到预分配的容量。在每个循环中,我们将循环变量i的当前值放置到numbers切片的下标中。

虽然append()cap()策略在功能上是等效的,但cap()的例子避免了使用append()函数所需的任何额外内存分配。

构造多维切片

您还可以将由其他切片组成的切片定义为元素,每个方括号中的列表都包含在父切片的大括号中。这样的切片集合称为多维切片。这些可以被认为是描绘多维坐标;例如,一个由5个长为6个元素的切片组成的集合可以表示一个水平长度为5、垂直高度为6的二维网格。

让我们来看看下面的多维切片:

seaNames := [][]string{{"shark", "octopus", "squid", "mantis shrimp"}, {"Sammy", "Jesse", "Drew", "Jamie"}}
  • 1

为了访问切片中的元素,我们需要使用多个索引,每个维度对应一个索引:

fmt.Println(seaNames[1][0])
fmt.Println(seaNames[0][0])
  • 1
  • 2

在上面的代码中,我们首先标识索引1处切片的索引0处的元素,然后标识索引0处切片的索引0处的元素。这将产生以下结果:

OutputSammy
shark
  • 1
  • 2

下面是其余各个元素的索引值:

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"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在处理多维切片时,一定要记住,为了访问相关嵌套切片中的特定元素,需要引用多个索引号。

总结

在本教程中,您学习了在Go中使用数组和切片的基础知识。我们通过多个练习演示了数组的长度是固定的,而切片的长度是可变的,并发现了这种差异如何影响在不同情况下对这些数据结构的使用。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/206344
推荐阅读
相关标签
  

闽ICP备14008679号