Golang Go语言中为什么建议多使用切片,少使用数组?

发布于 1周前 作者 wuwangju 来自 Go语言

大家好,我是 frank ,「 Golang 语言开发栈」公众号作者。

01 介绍

在 Go 语言中,数组固定长度,切片可变长度;数组和切片都是值传递,因为切片传递的是指针,所以切片也被称为“引用传递”。

读者朋友们在使用 Go 语言开发项目时,或者在阅读 Go 开源项目源码时,发现很少使用到数组,经常使用到切片。

本文通过讲解 Golang 切片的一些特性,介绍 Go 语言为什么建议多使用切片,少使用数组。

02 切片

切片的底层是数组,它是可变长度,可以在容量不足时自动扩容。

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

阅读上面这段代码,SliceHeader 结构体是切片在运行时的表现,由 3 部分组成,分别是指向底层数组的指针 Data,长度 Len 和容量 Cap

声明方式

切片的声明方式有多种,分别是:

var s1 []int
var s2 []int{1, 2, 3}
var s3 []int = make([]int, 3)
var s4 []int = make([]int, 3, 5)

阅读上面这段代码,s1 只声明未初始化,值为 nils2 字面量初始化,编译时会自动推断切片的长度,容量与长度相同;

s3 声明切片,并使用内置函数 make 初始化切片的长度为 3,因为未指定容量,所以容量与长度相同;s4 声明切片,并使用内置函数 make 初始化切片的长度为 3,切片的容量为 5,容量必须大于等于长度。

字面量初始化与使用内置函数 make 初始化的区别是,字面量初始化,编译时在数据区创建一个数组,并在堆区创建一个切片,程序启动时将数据区的数据复制到堆区;

使用内置函数 make 初始化,编译时根据切片大小判断分配到栈区,还是分配到堆区,小于 64KB 则分配到栈区,大于等于 64KB 则分配到堆区。

数组则是根据数组长度判定是否在栈区初始化,数组长度小于 4 时,编译时在栈区初始化数组。

“引用传递”

数组和切片在作为函数参数传递时,属于值传递,如果使用数组,特别是大数组时,我们需要特别小心,可以考虑使用数组指针;如果使用切片,本身就是拷贝的内存地址,所以切片也被称为“引用传递”。

自动扩容

切片可以使用内置函数 append 追加元素到切片,如果原切片容量不足时,切片可以自动扩容;数组是固定长度,如果数组长度不足时,编译时则报错,或者只能声明一个新数组,并将旧数组中的数据拷贝到新数组。

需要注意的是,虽然使用内置函数 append 追加元素,当切片容量不足时可以自动扩容切片,但是会涉及到内存分配,原切片容量小于 1024 ,新切片容量是原切片容量的 2 倍;

如果原切片容量大于等于 1024 ,新切片容量按照原切片容量的 1/4 步长循环扩容,直到新切片的容量大于等于新切片的长度为止。

03 总结

本文我们介绍 Go 语言为什么建议多使用切片,少使用数组。

主要是因为切片值传递的成本更低,更加适合作为函数参数,并且使用内置函数 append 追加切片元素时,当切片容量不足时可以自动扩容。

需要注意的是,虽然切片可以自动扩容,但在扩容时会涉及内存分配,造成系统开销,尽量在创建切片时,预估出切片的最终容量。


Golang Go语言中为什么建议多使用切片,少使用数组?

更多关于Golang Go语言中为什么建议多使用切片,少使用数组?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

19 回复

/t/1004805 我觉得你 append 就行了。而且如果你只是把 V2EX 当作又一个输出你公众号的平台,并不跟之前回复的人讨论,我感觉你可能会挨骂。

更多关于Golang Go语言中为什么建议多使用切片,少使用数组?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


怎么看着像 ai 写的。

> 数组和切片在作为函数参数传递时,属于值传递

> 如果使用切片,本身就是拷贝的内存地址,所以切片也被称为“引用传递”

什么意思?
值传递是函数传参按值,这里的引用传递也是是函数传参吗?

数组和切片都是值传递,因为切片传递的是指针,所以切片也被称为“引用传递”。 ??? 啥意思?切片到底是值传递 还是引用传递?

APPEND 不太好找对应主题

APPEND 不太好找对应主题
带引号的引用传递,因为虽然是值传递,但是传递的是指向数组的内存地址。

值传递,这里说的是带引号的引用传递


因为切片本身就是一种底层数组的指针,所以传递切片的值,相当于传递底层数组的指针。这块他确实没说明白,我觉得这个文章还需要打磨。

跟 php 的数组一个尿性,把屎当宝 he tui~

因为大部分场景是不固定长度的啊。真没多少场景是能提前知道需要多大的数组

"需要注意的是,虽然切片可以自动扩容,但在扩容时会涉及内存分配,造成系统开销,尽量在创建切片时,预估出切片的最终容量。"
这个切片的扩容和使用数组时重新分配一个更大的数组再拷贝一遍的花销相比怎么样?
另外, 从一个 初级 C 程序员的角度看来, 所有高级语言的这些特性都是华而不实的…

扩容的本质就是你说的这样,只不过切片的自动扩容会比较科学的帮你选取新数组的大小

没我的 AI 助手写的简洁,我分享一下它的回答:
+++

Go 语言建议多使用切片,少使用数组,主要是因为切片具有更大的灵活性和功能。以下是主要的原因:

1.动态长度:数组在定义时需要指定长度和元素类型,长度是固定的,不能动态改变。而切片是一个长度可变的数据类型,其长度在定义时可以为空,也可以指定一个初始长度。这使得切片在处理长度未知或可能变化的情况时更为方便 。

2.动态内存分配:数组的内存空间是在定义时分配的,其大小是固定的。而切片的内存空间是在运行时动态分配的,其大小是可变的。这使得切片在处理需要动态增长的数据时更为方便 。

3.函数参数传递:当数组作为函数参数时,函数操作的是数组的一个副本,不会影响原始数组。而当切片作为函数参数时,函数操作的是切片的引用,会影响原始切片。这使得切片在处理需要在函数内部修改数据的情况时更为方便 。

4.容量概念:切片还有容量的概念,它指的是分配的内存空间。这使得切片在处理需要管理内存使用的情况时更为方便 。

5.多维切片:切片可以通过分片的分片(或者切片的数组),长度可以任意动态变化,所以 Go 语言的多维切片可以任意切分。而且,内层的切片必须单独分配(通过 make 函数)。这使得切片在处理多维数据时更为方便 2 。

因此,Go 语言建议多使用切片,少使用数组,主要是因为切片具有更大的灵活性和功能。

起码泛型,模块和自动内存管理是好东西

一句话能说清楚,非要啰啰嗦嗦一长篇,还没写清楚,你用的这个 ai 明显水平还不到家呀

因为大道智减

同样的帖子 你发了几遍了吧

我也来贴个 AI 的
有几个原因为什么 Go 使用切片而不是数组。

* 切片更高效。 当你创建一个切片时,Go 不需要复制底层数组的所有元素。这是因为切片只是对底层数组的引用,切片的元素只有在修改时才会被复制。
* 切片更灵活。 你可以创建任何长度的切片,并且你可以随时更改切片的长度。这在数组中是不可能的,数组必须创建一个特定的长度。
* 切片更方便。 创建和使用切片比数组更容易。这是因为切片在创建时不需要指定数组的长度。

因此,Go 使用切片而不是数组。

在Golang中,建议多使用切片、少使用数组的原因主要基于以下几点:

  1. 灵活性:数组的长度是固定的,一旦声明就无法改变,这限制了其动态性。而切片则基于数组构建,但提供了动态增长的功能,通过内置的append函数可以方便地添加元素,无需担心数组越界或手动扩容的问题。
  2. 内存效率:切片是对底层数组的引用,多个切片可以共享同一个底层数组,从而节省内存空间。同时,切片在传递时只复制切片头部的结构体信息(包括指向底层数组的指针、长度和容量),而不会复制整个底层数组,这在传递大数据量时非常高效。
  3. 便捷性:切片提供了丰富的操作方法,如访问、修改、追加、删除和复制等,这些操作使得切片在处理数组时更加方便和直观。

综上所述,切片在Golang中具有更高的灵活性、内存效率和便捷性,因此在实际开发中建议多使用切片、少使用数组。当然,在某些特定场景下(如需要固定长度的数据集合时),数组仍然是合适的选择。但总体来说,切片是处理动态数组的更优选择。

回到顶部