Redian新闻
>
Go 数组和切片的介绍 | Linux 中国

Go 数组和切片的介绍 | Linux 中国

科技
 
导读:了解使用数组和切片在 Go 中存储数据的优缺点,以及为什么其中一个更好。                   
本文字数:4096,阅读时长大约:5分钟

了解使用数组和切片在 Go 中存储数据的优缺点,以及为什么其中一个更好。

在本系列的第四篇文章中,我将解释 Go🔗 golang.org 数组和切片,包括如何使用它们,以及为什么你通常要选择其中一个而不是另一个。

数组

数组是编程语言中最流行的数据结构之一,主要原因有两个:一是简单易懂,二是可以存储许多不同类型的数据。

你可以声明一个名为 anArray 的 Go 数组,该数组存储四个整数,如下所示:

  1. anArray := [4]int{-1, 2, 0, -4}

数组的大小应该在它的类型之前声明,而类型应该在声明元素之前定义。len() 函数可以帮助你得到任何数组的长度。上面数组的大小是 4。

如果你熟悉其他编程语言,你可能会尝试使用 for 循环来遍历数组。Go 当然也支持 for 循环,不过,正如你将在下面看到的,Go 的 range 关键字可以让你更优雅地遍历数组或切片。

最后,你也可以定义一个二维数组,如下:

  1. twoD := [3][3]int{
  2. {1, 2, 3},
  3. {6, 7, 8},
  4. {10, 11, 12}}

arrays.go 源文件中包含了 Go 数组的示例代码。其中最重要的部分是:

  1. for i := 0; i < len(twoD); i++ {
  2. k := twoD[i]
  3. for j := 0; j < len(k); j++ {
  4. fmt.Print(k[j], " ")
  5. }
  6. fmt.Println()
  7. }
  8. for _, a := range twoD {
  9. for _, j := range a {
  10. fmt.Print(j, " ")
  11. }
  12. fmt.Println()
  13. }

通过上述代码,我们知道了如何使用 for 循环和 range 关键字迭代数组的元素。arrays.go 的其余代码则展示了如何将数组作为参数传递给函数。

以下是 arrays.go 的输出:

  1. $ go run arrays.go
  2. Before change(): [-1 2 0 -4]
  3. After change(): [-1 2 0 -4]
  4. 1 2 3
  5. 6 7 8
  6. 10 11 12
  7. 1 2 3
  8. 6 7 8
  9. 10 11 12

这个输出告诉我们:对函数内的数组所做的更改,会在函数退出后丢失。

数组的缺点

Go 数组有很多缺点,你应该重新考虑是否要在 Go 项目中使用它们。

首先,数组定义之后,大小就无法改变,这意味着 Go 数组不是动态的。简而言之,如果你需要将一个元素添加到一个没有剩余空间的数组中,你将需要创建一个更大的数组,并将旧数组的所有元素复制到新数组中。

其次,当你将数组作为参数传递给函数时,实际上是传递了数组的副本,这意味着你对函数内部的数组所做的任何更改,都将在函数退出后丢失。

最后,将大数组传递给函数可能会很慢,主要是因为 Go 必须创建数组的副本。

以上这些问题的解决方案,就是使用 Go 切片。

切片

Go 切片与 Go 数组类似,但是它没有后者的缺点。

首先,你可以使用 append() 函数将元素添加到现有切片中。此外,Go 切片在内部使用数组实现,这意味着 Go 中每个切片都有一个底层数组。

切片具有 capacity 属性和 length 属性,它们并不总是相同的。切片的长度与元素个数相同的数组的长度相同,可以使用 len() 函数得到。切片的容量是当前为切片分配的空间,可以使用 cap() 函数得到。

由于切片的大小是动态的,如果切片空间不足(也就是说,当你尝试再向切片中添加一个元素时,底层数组的长度恰好与容量相等),Go 会自动将它的当前容量加倍,使其空间能够容纳更多元素,然后将请求的元素添加到底层数组中。

此外,切片是通过引用传递给函数的,这意味着实际传递给函数的是切片变量的内存地址,这样一来,你对函数内部的切片所做的任何修改,都不会在函数退出后丢失。因此,将大切片传递给函数,要比将具有相同数量元素的数组传递给同一函数快得多。这是因为 Go 不必拷贝切片 —— 它只需传递切片变量的内存地址。

slice.go 源文件中有 Go 切片的代码示例,其中包含以下代码:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func negative(x []int) {
  6. for i, k := range x {
  7. x[i] = -k
  8. }
  9. }
  10. func printSlice(x []int) {
  11. for _, number := range x {
  12. fmt.Printf("%d ", number)
  13. }
  14. fmt.Println()
  15. }
  16. func main() {
  17. s := []int{0, 14, 5, 0, 7, 19}
  18. printSlice(s)
  19. negative(s)
  20. printSlice(s)
  21. fmt.Printf("Before. Cap: %d, length: %d\n", cap(s), len(s))
  22. s = append(s, -100)
  23. fmt.Printf("After. Cap: %d, length: %d\n", cap(s), len(s))
  24. printSlice(s)
  25. anotherSlice := make([]int, 4)
  26. fmt.Printf("A new slice with 4 elements: ")
  27. printSlice(anotherSlice)
  28. }

切片和数组在定义方式上的最大区别就在于:你不需要指定切片的大小。实际上,切片的大小取决于你要放入其中的元素数量。此外,append() 函数允许你将元素添加到现有切片 —— 请注意,即使切片的容量允许你将元素添加到该切片,它的长度也不会被修改,除非你调用 append()。上述代码中的 printSlice() 函数是一个辅助函数,用于打印切片中的所有元素,而 negative() 函数将切片中的每个元素都变为各自的相反数。

运行 slice.go 将得到以下输出:

  1. $ go run slice.go
  2. 0 14 5 0 7 19
  3. 0 -14 -5 0 -7 -19
  4. Before. Cap: 6, length: 6
  5. After. Cap: 12, length: 7
  6. 0 -14 -5 0 -7 -19 -100
  7. A new slice with 4 elements: 0 0 0 0

请注意,当你创建一个新切片,并为给定数量的元素分配内存空间时,Go 会自动地将所有元素都初始化为其类型的零值,在本例中为 0(int 类型的零值)。

使用切片来引用数组

Go 允许你使用 [:] 语法,使用切片来引用现有的数组。在这种情况下,你对切片所做的任何更改都将传播到数组中 —— 详见 refArray.go。请记住,使用 [:] 不会创建数组的副本,它只是对数组的引用。

refArray.go 中最有趣的部分是:

  1. func main() {
  2. anArray := [5]int{-1, 2, -3, 4, -5}
  3. refAnArray := anArray[:]
  4. fmt.Println("Array:", anArray)
  5. printSlice(refAnArray)
  6. negative(refAnArray)
  7. fmt.Println("Array:", anArray)
  8. }

运行 refArray.go,输出如下:

  1. $ go run refArray.go
  2. Array: [-1 2 -3 4 -5]
  3. -1 2 -3 4 -5
  4. Array: [1 -2 3 -4 5]

我们可以发现:对 anArray 数组的切片引用进行了操作后,它本身也被改变了。

总结

尽管 Go 提供了数组和切片两种类型,你很可能还是会使用切片,因为它们比 Go 数组更加通用、强大。只有少数情况需要使用数组而不是切片,特别是当你完全确定元素的数量固定不变时。

你可以在 GitHub🔗 github.com 上找到 arrays.goslice.go 和 refArray.go 的源代码。

如果你有任何问题或反馈,请在下方发表评论或在 Twitter🔗 twitter.com 上与我联系。


via: https://opensource.com/article/18/7/introduction-go-arrays-and-slices

作者:Mihalis Tsoukalos 选题:lkxed 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出


LCTT 译者 :六开箱
🌟🌟🌟🌟
翻译: 70.0 篇
|
贡献: 78 天
2022-03-16
2022-06-02
https://linux.cn/lctt/lkxed
欢迎遵照 CC-BY-SA 协议规定转载,
如需转载,请在文章下留言 “转载:公众号名称”,
我们将为您添加白名单,授权“转载文章时可以修改”。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
在美国62. 我会偷?【英译唐诗】白居易《忆江南三首》实测 Linux Mint 升级工具 | Linux 中国Fedora Linux 36 发布 | Linux 中国在虚拟机中运行 Linux 的十大优点 | Linux 中国我如何在 Linux 上扫描家庭照片 | Linux 中国最适合程序员的 10 款 Linux 发行版 | Linux 中国System76 与惠普合作为开发者提供功能强大的 Linux 笔记本电脑 | Linux 中国人有人格,坛有坛格---各显其格。。。如何在 Linux 桌面中启用 “激活 Linux” 水印通知 | Linux 中国上一个说“丼”不读jǐng的人,已经被我骂哭了Linux 中国开通播客频道:“开源朗读者”和“硬核观察” | Linux 中国微软还有另一个 Linux 发行版,而且是基于 Debian 的 | Linux 中国使用 watch 和 tail 命令监视 Linux 上的活动 | Linux 中国陝西出的大作家(2)用 Gwenview 在 Linux 上裁剪和调整照片大小 | Linux 中国使用 apt 进行 Linux 包管理 | Linux 中国Linux 内核 5.18 版本正式发布,新增显卡驱动以及硬件支持 | Linux 中国分享 8 篇使用 Linux 命令行的技巧 | Linux 中国使用 dnf 进行 Linux 包管理 | Linux 中国如何在 Linux 和 Windows 电脑之间共享文件 | Linux 中国好消息!Docker Desktop 现已支持 Linux | Linux 中国英伟达在提升 Linux 上的 GPU 使用体验上迈出了一大步 | Linux 中国你的 Linux 启动时有几只小企鹅? | Linux 中国马克谈天下(272) 香港疫情的6点经验教训Collision:用于验证 ISO 和其他文件的 Linux 应用 | Linux 中国如何在 Fedora Linux 中安装多媒体编码器 | Linux 中国HydraPaper:一个支持多显示器的 Linux 壁纸管理器 | Linux 中国Fudgie?令人惊叹的 Budgie 桌面即将登陆 Fedora Linux | Linux 中国Plex 桌面播放器现已支持 Linux | Linux 中国Linux Lite 6.0 发布:弃用 Firefox,默认浏览器使用 Chrome | Linux 中国Archinstall 新的菜单系统让安装 Arch Linux 更容易了 | Linux 中国在 Linux 上使用 sudo 命令的 5 个理由 | Linux 中国CentOS 的继承者 AlmaLinux 9 发布 | Linux 中国在 Ubuntu Linux 如何安装 H.264 解码器 | Linux 中国
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。