Go入门(7)——切片

mb6066e165689bf · · 116 次点击 · · 开始浏览    

切片与数组的关系

Go入门(7)——切片

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()创建切片

当相关数组还没有定义时,可以使用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()和make()的区别

二者都在堆上分配内存,但是行为不同,适用类型不同。

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函数)。

bytes包

类型[]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

通过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/Copy切片

数组长度是固定的;切片是动态的。使用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]

有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:mb6066e165689bf

查看原文:Go入门(7)——切片

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

116 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传