-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
数组 Array 与切片Slice #8
Comments
Go Slices 切片Go slices 切片类型为处理类型化数据序列提供了一种方便而有效的方法。Slices 非常类似于其它语言的数组类型,但又有一些非常用的属性。这篇文章主要就是介绍 slices 是如何使用的。 数组slices 是一个构建在 Go 数组类型之上的一种抽象类型,所以为了更好的理解 slice 类型,我们有必要先理解数组类型。 数字类型指定了数组长度与元素类型。例子, var a [4]int
a[0] = 1
i := a[0]
// i == 1 数组不需要显式的初始化;数组元素的零值是默认初始值:
Go 数组是值。数组变量表示是整个数组;它不是指向第一个数组元素的指针(就像 C 语言中的一样)。就是说当你要分配或传递一个数组值时,会对数组的内容进行拷贝。(为了避免拷贝,你可以传递一个数组指针,但是这个是指向的数组的指针而不是数组)。考虑数组的一种方法是,它是一种结构体,但带有索引字段而不是命名字段:一个固定大小的复合值。 数组字面量可以如下指定:
或者你可以靠编译器统计你数组的元素:
上面两种都代表的是 Slices数组有它们自己的应用场景,但是还是有点不灵活,所以你在 Go 代码中经常看不到这些数组代码。反而 Slices 到处都是。它构建在数组之上,更加有力量和方便的。 Slices 类型说明是 slice 字面量可以像数组那样申明,只是你要省去元素计数:
slice 可以通过 Go 内置的 make 创建:
其中 T 表示创建的 slice 的元素类型。
当 cap 参数省略时,默认就会只当一个长度。下面是同一版本更简单的版本代码:
len 和 cap 参数可以通过内置的
下面两节来讨论 len 和 cap 的关系。 slice 的零值是 slice 也能将已有的 slice 对象以及数组对象通过”切片“构成 slice。切片是通过指定一个半开范围(half-open range),两个下标用冒号隔开来完成的。例如,表达式
切片表达式的开始与结束参数是可选的;默认情况是从 0 到 slice 的长度:
这里也有语法支持从给定的数组创建一个 slice:
Slice 内部细节slice 是数组段的描述符,它由指向数组的指针、段的长度和它的容量(段的最大长度)组成。 我们变量 s,通过 长度通过 slice 指向元素的数量。capacity 是指在底层数组的元素数量(从切片指针所指向的元素开始)。为了更加清晰的区分 len 与 cap 两者的区别,我们通过下面的例子来说明。 我们有一个 slice 变量 s,观察 slice 数据结构的变化以及在其底层数组的关系:
切片操作并没有发生数据的拷贝。它创建了新的 slice 值,它指向的原始数组对象。这使得切片操作与数组索引操作一样高效。因此修改 slice 中的元素,就修改数组的原始元素:
早先我们把 s 切成的长度比它的容量短的长度。我们可以通过再次切割来达到它的容量:
一个切片不能超过它的容量。如果要这么做就会发生一个运行时的 panic,只需要索引设置成 slice 和数组的边界之外就能发生错误。同样的,slice 不能重切片至 0 以下以访问数组元素。 增长切片(拷贝与追加函数)为了增加切片的容量,必须创建一个新的、更大的切片,并将原始切片的内容复制到其中。这个技术在其它语言也是动态数组的实现。下面的例子是通过构建新的 slice 将原来的容量翻倍,拷贝 s 的元素到 t,然后分配切片值 t 到 s:
内置的复制函数使这个常见操作的循环部分变得更容易。顾名思义,copy 将数据从源片复制到目标片。它返回复制的元素个数。
copy 函数支持两个长度不一的切片(它只会拷贝元素数量小的)。另外,copy 能处理源和目标 slice 共享相同的底层数组,即能够正确处理重叠的 slice。 通过 copy,我们可以高效的使用下面代码代替上面的代码:
一个通用的操作就是追加数据到 slice 的尾部。这个函数追加字节元素到字节类型的 slice 对象,如必要就会增长 slice 并返回已经更新的 slice 值: func AppendByte(slice []byte, data ...byte) []byte {
m := len(slice)
n := m + len(data)
if n > cap(slice) { // if necessary, reallocate
// allocate double what's needed, for future growth.
newSlice := make([]byte, (n+1)*2)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:n]
copy(slice[m:n], data)
return slice
} 使用方式:
但绝大多数程序都不需要完全控制,所以 Go 提供了内置了
append 函数将元素 x 附加到切片 s 的末尾,如果需要更大的容量,则增加切片。
将一个 slice 追加到另一个 slice,可以使用
由于 slice 的零值的行为很象长度为 0 的 slice,你可以申明一个 slice 变量并在循环中追加元素:
潜在的"陷阱"如前所述,对切片进行重新切片并不会生成底层数组的副本。整个数组将一直保存在内存中,直到它不再被引用。偶尔,这可能会导致程序在只需要一小部分数据时将所有数据保存在内存中。 例如,
这段代码的行为与发布的一样,但是返回的 为了修复这种问题,我们可以在它返回之前只对有兴趣的内容进行拷贝:
|
https://go.dev/blog/slices-intro
The text was updated successfully, but these errors were encountered: