Golang数组和切片的完整指南

本文的翻译是专门为Golang Developer课程的学生准备的, 课程从今天开始!




刚开始时,很容易将数组和切片视为相同,但是名称不同:两者都是表示集合的数据结构。 但是,实际上它们彼此非常不同。

在本文中,我们将研究它们在Go中的区别和实现。

我们将转向示例,以便您可以更明智地决定将其应用于何处。

数组


数组是固定大小的集合。 这里的重点放在固定大小上,因为一旦您设置了数组的长度,以后您将无法更改它。

让我们来看一个例子。 我们将创建一个包含四个整数值的数组:

arr := [4]int{3, 2, 5, 4} 


长度和类型


在上面的示例中,变量arr定义为类型[4]int的数组,这意味着该数组由四个元素组成。 重要的是要注意,类型定义中包括了大小4

因此,实际上,不同长度的数组是不同类型的数组。 在这种情况下,不能将不同长度的数组彼此等同,也不能将一个数组的值分配给另一个数组:

 longerArr := [5]int{5, 7, 1, 2, 0} longerArr = arr // This gives a compilation error longerArr == arr // This gives a compilation error 


我发现数组在结构方面很容易谈论。 如果尝试创建类似于数组的结构 ,则很可能会得到以下信息:

 // Struct equivalent for an array of length 4 type int4 struct { e0 int e1 int e2 int e3 int } // Struct equivalent for an array of length 5 type int5 struct { e0 int e1 int e2 int e3 int e5 int } arr := int4{3, 2, 5, 4} longerArr := int5{5, 7, 1, 2, 0} 

实际上,不建议这样做,但这是了解为什么不同长度的数组是不同类型的数组的好方法。


内存表示


该数组存储为特定类型的n个块的序列:



初始化数组变量时将分配此内存。

通过链接传递


Go没有通过引用传递的东西;相反,所有东西都是通过值传递的。 如果将数组的值分配给另一个变量,则将简单地复制分配的值。



如果仅要将“引用”传递给数组,请使用指针:



在分配内存和函数时,数组实际上是一种简单的数据类型,其工作方式与结构非常相似。

切片


切片可以视为数组的扩展实现。
Go中实现了切片,以覆盖开发人员在使用集合时遇到的一些非常常见的用例,例如动态调整集合的大小。

slice声明与数组声明非常相似,只是省略了长度说明符:

 slice := []int{4, 5, 3} 


如果仅看代码,似乎切片和数组非常相似,但是它们的主要区别在于实现和使用条件。

内存表示


切片的分配方式与数组不同,本质上是修改后的指针。 每个切片包含三个信息块:

  1. 指向数据序列的指针。
  2. 长度,该长度确定切片中当前包含的元素数。
  3. 容量(capacity),它确定提供的存储单元的总数。




因此,可以将不同长度的切片彼此分配。 它们是同一类型,并且指针,长度和音量可以变化:

 slice1 := []int{6, 1, 2} slice2 := []int{9, 3} // slices of any length can be assigned to other slice types slice1 = slice2 


切片与数组不同,在初始化期间不会分配内存。 实际上,切片使用nil值初始化。

通过链接传递


当您将切片分配给另一个变量时,您仍在传递值。 在此,该值仅指指针,长度和体积,而不指元素本身占用的内存。



新增项目


要将新元素添加到切片,必须使用append函数。

 nums := []int{8, 0} nums = append(nums, 8) 


在幕后,看起来就像为新元素分配一个指定的值,然后–返回新的slice。 新切片的长度将增加一倍。



如果在添加元素时长度增加了一个,从而超过了声明的体积,则有必要提供一个新体积(在这种情况下,当前体积通常会加倍)。

这就是为什么通常建议使用预先指定的长度和体积创建切片的原因(尤其是如果您清楚地知道所需的切片大小):

 arr := make([]int, 0, 5) // This creates a slice with length 0 and capacity 5 


使用什么:数组或切片?


数组和切片是完全不同的东西,因此它们的用例也有所不同。

让我们看一些开源示例和Go标准库,以了解使用什么以及何时使用。

情况1:UUID


UUID是128位数据,通常用于标记对象或实体。 通常,它们以十六进制值表示,并用破折号分隔:

 e39bdaf4-710d-42ea-a29b-58c368b0c53c 


Google UUID库中,UUID表示为16个字节的数组:

 type UUID [16]byte 

这是有道理的,因为我们知道UUID由128位(16个字节)组成。 我们不会在UUID中添加或删除任何字节,因此将使用数组来表示它。

情况2:对整数值进行排序


在此示例中,我们将使用sort标准库中sort.Ints函数:

 s := []int{5, 2, 6, 3, 1, 4} // unsorted sort.Ints(s) fmt.Println(s) // [1 2 3 4 5 6] 


sort.Ints函数采用一片整数,然后按值的升序对其进行排序。 切片最好在这里使用,原因有两个:

  1. 未指定整数数(排序的整数数可以是任意数);
  2. 数字需要按升序排序。 使用数组将确保将整数的整个集合作为值传递,因此该函数将对自己的副本进行排序,而不是传递给它的集合。


结论


现在,我们已经研究了数组和切片之间的关键区别以及它们的用例,我想提供一些提示,使您可以更轻松地决定使用哪种设计:

  1. 如果一个实体由一组固定长度的非空元素描述,则使用数组。
  2. 描述要添加或从中删除项目的集合时,请使用切片。
  3. 如果集合可以包含任意数量的元素,请使用切片。
  4. 您会以任何方式更改收藏吗? 如果是这样,则应使用切片。


如您所见,切片涵盖了构建Go应用程序的大多数情况。 但是,数组有生存的权利,而且,它们非常有用,尤其是在出现合适的用例时。

Source: https://habr.com/ru/post/zh-CN465613/


All Articles