Go语言中切片是对数组的抽象。Go数组的长度不可改变,在特定场景中可能显得比较笨重。Go提供了一种灵活的、功能强悍的内置类型切片,可理解为动态数组,其长度不固定且可以追加元素。追加元素可能导致切片容量增大。
切片是对数组中一个连续片段的引用(这里所谓的数组被称为“相关数组”,通常是匿名的),所以切片是一个引用类型(类似于C/C++的数组类型、Python的list
类型)。这个切片可以是整个数组,或者是由起始和终止索引标识的一些项形成的子集(终止索引标识的项并不包括在切片内)。切片提供了一个相关数组的动态窗口。
可以声明未指定大小的数组来定义切片,切片不需要说明长度。
var identifier []type
或者使用make()
函数来创建切片:
var slice1 []type = make([]type, len)
// 也可以简写成:
slice1 := make([]type, len)
也可以指定容量,其中capacity
为可选参数:
make([]T, length, capacity)
如果有函数需要对数组进行操作,那这个数组基本上要设定为切片。如下计算数组元素和:
func sum(a []int) int {
s := 0
for i:=0; i<len(a); i++ {
s += a[i]
}
return s
}
func main() {
var arr = [5]int{0,1,2,3,4}
sum(arr[:])
}
当相关数组还没有定义时,可以使用make()
函数来创建一个切片,同时创建好相关数组:
var slice1 []type = make([]type, len)
也可以简写为:
slice1 := make([]type, len)
这里的len
是数组的长度,也是slice
的初始长度。
若定义s2 := make([]int, 10)
,则有cap(s2) == len(s2) == 10
。
make
接受2个参数:元素的类型以及切片的元素个数。
如果需要创建一个slice1
,但是不占用整个数组,而是占用len
个项,则应:
slice1 := make([]type, len, cap)
make
的使用方式即:
func make([]T, len, cap)
其中cap
为可选参数。
以下两种方式可以生成相同的切片:
make([]int, 50, 100)
new([100]int)[0:50]
以下代码描述了使用make
产生的切片的内存结构:
// make_slice.go
package main
import "fmt"
func main() {
var slice1 []int = make([]int, 10)
// 加载数组/切片
for i:=0; i<len(slice1); i++ {
slice1[i] = i * 5
}
// 打印切片
for i:=0; i<len(slice1); i++ {
fmt.Printf("Slice at %d is %d \n", i, slice1[i])
}
fmt.Printf("\nThe length of slice1 is %d\n", len(slice1))
fmt.Printf("The capacity of slice1 is %d\n", cap(slice1))
}
输出为:
Slice at 0 is 0
Slice at 1 is 5
Slice at 2 is 10
Slice at 3 is 15
Slice at 4 is 20
Slice at 5 is 25
Slice at 6 is 30
Slice at 7 is 35
Slice at 8 is 40
Slice at 9 is 45
The length of slice1 is 10
The capacity of slice1 is 10
另外,因为字符串是纯粹不可变的字节数组,所以也可以被切分为切片。
二者都在堆上分配内存,但是行为不同,适用类型不同。
new(T) |
make(T) |
---|---|
为每个新的类型T 分配内存,初始化为0 ,并且返回类型为*T 的内存地址。这种方法返回一个指向类型为T,值为0的地址的指针,适用于值类型(数组、结构体),相当于&T{} 。 |
返回一个类型为T的初始值,只适用于3中内建的引用类型:切片(slice)、集合(map)、通道(channel)。 |
即:new
函数分配内存,make
函数初始化。
var p *[]int = new ([]int) // *p == nil; with len and cap 0
p := new([]int)
p := make([]int, 0)
意即切片已被初始化,但指向数组为空。
和数组一样,切片通常也是一维的,但是可以通过组合成为高维。通过切片的分片(或者切片的数组),长度可以任意动态变化。内层的切片必须单独分配(通过make
函数)。
类型[]byte
的切片十分常见,Go中有个专门的bytes
包来解决这种类型的操作问题。
bytes
包和字符串包十分类似,且包含一个十分有用的类型Buffer
。
import "bytes"
type Buffer struct {
// ...
}
这是一个长度可变的bytes
的Buffer
,提供Read
和Write
方法,因为读写长度未知的bytes
最好使用Buffer
。
Buffer
可以使用如下的方式定义:
var buffer bytes.Buffer
// 或者使用new获得一个指针
var r *bytes.Buffer = new(bytes.Buffer)
// 或者通过函数创建Buffer并用buf初始化
// NewBuffer最好在从buf读取时使用
func NewBuffer(buf []byte) *Buffer
类似于Java的StringBuilder
类。
下面的代码中,创建一个Buffer
,通过buffer.WriteString(s)
方法将字符串s
追加到最后面,最后通过buffer.String()
方法转换为string
:
var buffer bytes.Buffer
for {
if s, ok := getNextString(); ok { // getNextString()方法
buffer.WriteString(s)
} else {
break
}
}
fmt.Print(buffer.String(), "\n")
数组长度是固定的;切片是动态的。使用append
可以将新元素追加到切片上。append
的定义是:
func append (s[]T, x ... T) []T
append
可以直接在切片尾部追加元素,也可以将一个切片追加到另一个切片尾部。
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
// 允许追加空切片
numbers = append(numbers, 0)
printSlice(numbers)
// 向切片添加一个元素
numbers = append(numbers, 1)
printSlice(numbers)
// 同时添加多个元素
numbers = append(numbers, 2, 3, 4)
printSlice(numbers)
// 创建切片numbers1,是之前切片的两倍容量
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
// 拷贝numbers的内容到numbers1
copy(numbers1, numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v \n", len(x), cap(x), x)
}
输出为:
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]
原文:https://www.cnblogs.com/4thrun/p/Golang_7.html