切片(slice) 1年前

编程语言
951
切片(slice)

Go语言切片

上一小节介绍了Go语言的数组,数组是固定大小的具有相同类型的序列,在内存中,就是一块连续的内存

但是由于数组是固定大小的,使用上有些限制,所以在Go语言中,数组使用的不是很频繁

Go语言提供了一个类似Java语言中的 List ,类似 C++ 中的 vector ,类似 Python中的列表[]

这种数据结构就是切片,英文是 slice

Go语言中的切片就是动态数组,没有固定大小,可以删除,追加元素,原理和 Java语言中的 ArrayList 一样

1 定义切片

切片切片使用[]dataType 定义,其中 [] 是必须的,dataType 代表的是切片中元素的数据类型。

Go语言切片的定义有 2 种方式,

先看第一种:var slice []类型

1.1 直接通过 var 声明

语法如下:

var 名字 []类型 ,比如var s []int

示例如下:

//定义了一个切片 s , 里面的元素类型是 int 
var s []int

fmt.Println(s)     //输出: []

上面声明了一个切片s,元素类型都是int

打印出来的是个空,这种方式声明的切片,默认值是 nil ,长度为 0

可以看下面的例子,验证一下:

func main() {
    var slice []int
    if slice == nil {
        fmt.Println("slice == nil ")
    }
}

//输出: slice == nil

1.2 通过 make 声明切片

通过make函数定义切片

语法为:make([]类型,len, cap)

其中

  • 类型 就是里面元素的数据类型
  • len 是必填参数,代表切片的大小
  • cap 是可选参数,代表切片的容量,如果不填,默认等于len,如果填写,必须满足 cap >= len

示例如下:

func main() {
    s := make([]int,5)
    
    fmt.Println(s)    //输出: [0 0 0 0 0]
}

从上面可知:

  • 通过make函数定义了一个 len = 5 , 类型为 int 的切片 s

  • 此时 s 不为 nil 了 ,因为打印出来的是有 5 个元素的切片

  • 切片中每个元素的默认值是相应类型的默认值, int 为 0 , string 为 ""

2 切片和数组的区别

切片是“动态数组”,没有固定的大小,可以往里面追加元素,随着元素的增多,底层会自动扩容

数组是有固定的大小,里面能存放多少元素是一定的

很多人会把 切片和数组的定义弄混淆分不清楚。这里面有一个很好的区分方法。

我们先回顾一下数组和切片的定义,然后再讲区分方法

或者切片

2.1 数组的定义

分别定义一个 元素的类型为int的,大小为 5 的数组

如下 4 种方法:

  • var arr = [5]int
  • var arr = [...]{1,2,3,4,5}
  • arr := [5]int {1,2,3,4,5}
  • arr := [...]int {1,2,3,4,5}

2.2 切片的定义

和上面一样:分别定义一个 元素的类型为int的,len 为 5 的切片

如下 2 种方法:

  • var s []int
  • s := make([]int,5)

通过上面 数组切片 的定义可以知道,他们的区别就在于 [ ] 里面是否有 "东西"

对于数组:不管哪种定义方式,[ ] 里面要么是一个,比如 var arr = [5]int ,要么是 ... , 比如var arr = [...]{1,2,3,4,5}

​ 总之,数组的定义,[ ] 里面必须有个 "东西" , 或者 ...

对于切片: 不管哪种定义方式,[ ] 里面没有 "东西" ,比如var s []int 或者 s := make([]int,5) , [ ] 里面啥也没有

所以,切片 和 数组 定义的区别在于: [ ] 中是否有东西,有东西的是数组,没有东西的是切片

3 切片截取

切片支持索引读取,通过索引截取,可以从 数组上面截取,也可以从切片上截取,返回的结果还是一个切片

3.1 切片的读取

像数组一样,通过索引对切片进行赋值,读取。 索引从 0 开始

示例如下:

func main() {
    s := make([]int,5)
    fmt.Println(s)        //输出: [0 0 0 0 0]

    s[1] = 13
    fmt.Println(s)        //输出:[0 13 0 0 0]
}

3.2 切片的截取

切片可以从数组,切片中,截取一部分,成为一个新的切片

语法为:newSlice := s[ start : end ]

其中:

  • newSlice 是截取后的切片
  • s是切片或者数组
  • start , end 是索引,前包括,后不包括,即前开后闭
  • 中间使用冒号 : 分隔

如下示例:

func main() {

    arr := [7]int{ 0 , 1, 2, 3, 4, 5, 6}

    s := arr[1:5]
    fmt.Println( s )     //输出:[1 2 3 4]

    //从索引 2 一直到最后
    s1 := arr[2:]
    fmt.Println(s1)        //输出:[2 3 4 5 6]

    //从开始的 0 到索引 4 (不包括 4 )
    s2 := arr[:4]
    fmt.Println( s2 )    //输出:[0 1 2 3]

    //从 0 到最后,等于把整个数组截取了
    s3 := arr[:]
    fmt.Println( s3 )    //输出:[0 1 2 3 4 5 6]

    //从切片 s 上截取
    s4 := s[1:3]
    fmt.Println(s4)        //输出:[2 3]
}

3.3 切片的添加

向切片中添加元素,使用内置的 append() 函数

由于 append() 函数返回的是一个新的 切片,所以需要再接收一下

看下面的例子就明白了:

func main() {
    var s []int

    // 必须再重新赋值给 s
    // 否则会报错
    s = append(s, 1)
    s = append(s, 2)
    s = append(s, 3)

    fmt.Println(s)
}

//输出:[1 2 3]

从上面可知:

  • append 返回一个新的切片,必须再用 s 接收,否则 s 里面不会添加元素,如下: s 打印出来还是 [ ]

    func main() {
        var s []int
    
        s2 := append(s, 1)
    
        fmt.Println("s=" ,s)    
        fmt.Println("s2=",s2)
    }
    
    //输出如下:
    s= []
    s2= [1]
    
  • var s []int 声明的切片是 nil , 但是在Go语言里面,是可以添加元素直接使用的,并不会报错

3.4 切片的长度和容量

Go语言中,切片是“动态的数组”,在切片被定义时,就已经为切片分配了一个一定长度的数组。

当切片添加的元素超过了底层的数组的大小时,Go语言会再分配一个更大的数组给切片引用。

所以切片的长度和容量分别是:

  • 长度 就是切片中的元素的数量,使用内置的 len()函数获取切片长度

  • 容量就是切片底层引用的数组的大小,使用内置的cap()函数获取切片的容量

  • 容量大于等于长度 ,即 cap >= len

  • 定义切片时,如果不指定容量,则默认 cap == len

示例如下:

func main() {
    var s = []int {1,2,3}
    fmt.Println( "长度=",len(s), "容量=",cap(s) )        //输出:长度= 3 容量= 3

    s = append(s, 4)
    fmt.Println( "长度=",len(s), "容量=",cap(s) )        //输出:长度= 4 容量= 6

    s = append(s, 4,5,6)
    fmt.Println( "长度=",len(s), "容量=",cap(s) )        //输出:长度= 7 容量= 12
}

//输出如下:
长度= 3 容量= 3
长度= 4 容量= 6
长度= 7 容量= 12

从上面可以看到,随着元素的添加,超过切片的容量之后,切片引用的底层数组的大小变为了原来的 2 倍

Go语言切片总结

通过上面的介绍,Go语言的切片是一个动态的可增长的数据结构,和Java中的 ArrayList 类似,和C++中的 vector 类似

现总结如下:

  • Go语言切片定义有 2 种方式

    1. 通过 var 方式,比如 var s []int
    2. 通过make 函数方式,比如 make([]int,5) , 第2个参数为长度,第3个参数可以省略,省略默认和长度相等
  • 通过var s []int 声明的切片默认值为 nil ,但是可以直接添加元素,不会报错,如 s = append(s,10)

  • 切片可以从数组或者切片中,截取一段,返回一个新的切片

  • 切片截取语法为:[start: end] ,前包括后不包括,如 s1 := s[1:3] 取 s 中的 索引为 0,1,2 的元素

  • 切片的访问通过索引,索引从 0 开始

  • 向切片添加元素,使用内置的 append()函数,注意,append()返回的切片必须重新赋值给原来的切片

    s = append(s,10) , 必须重新赋值给 s

  • 切片的长度和容量分别使用 len() 函数和 cap() 函数

image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223198
累计阅读

热门教程文档

Docker
62小节
Rust
84小节
C
14小节
爬虫
6小节
MySQL
34小节