ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

《Go程序设计语言》学习笔记之slice

2021-12-20 09:35:35  阅读:146  来源: 互联网

标签:slice int fmt len Println 数组 Go 程序设计


《Go程序设计语言》学习笔记之slice

 

一. 环境

  Centos8.5, go1.17.5 linux/amd64

 

二. 概念

  slice 表示一个拥有相同类型元素的可变长度的序列。slice 通常写成 []T,其中元素的类型是T,它看上去像没有长度的数组类型。

 

slice 有三个属性:指针、长度、容量。

指针,指向数组的第一个可以从 slice 中访问的元素,这个元素并不一定是数组的第一个元素。

长度,slice 中元素的个数。

容量,从 slice 的起始元素到底层数组最后一个元素间元素的数量。

 

底层数组与 slice 是一对多的关系,一个底层数组可以对应多个 slice ,这些 slice 可以引用数组的任何位置,彼此之间的元素还可以重叠。

slice 操作符 s[i:j] (其中 0 <= i <= j <= cap(s)) 创建了一个新的 slice ,这个新的 slice 引用了序列 s 中从 i 到 j-1 索引位置的所有元素,这里的 s 既可以是数组或者指向数组的指针,也可以是 slice 。新 slice 的元素个数是 j-i 个。

s[i:j]

 

零值

slice 类型的零值是 nil。

 

三.申明/初始化

var s []int

 

 

1) slice s 指向数组。第9行代码申明了一个 slice s,它与数组的声明很像,但是 [] 中没有指定长度。结果是创建了一个指向数组的 slice。

  8 func main() {
  9     s := []int{0, 1, 2, 3, 4, 5}
 10     fmt.Printf("type: %T\n", s)
 11     fmt.Printf("len: %d\n", len(s))
 12     fmt.Printf("cap: %d\n", cap(s))
 13 }

  运行结果如下

 

 

 2) slice s 指向数组的指针

 

3) slice s 指向 数组的slice 

 

4) 内置函数 make 可以创建一个具有指定元素类型、长度和容量的 slice 。其中容量参数可以省略,此时,slice 的容量和长度相等。make 其实是创建了一个无名数组并返回了它的一个 slice,这个数组仅可能通过这个 slice 来访问。 

示例代码如下

通过打印结果可知,slice 变量 s1 的容量没有指定,默认和其长度一致,两者均为5。slice 变量 s2 仅引用了数组前 5 个元素,但是它的容量是数组的长度,slice 的长度还有增长的空间。slice 变量 s3 与 s2 功能相同。

  8 func main() {
  9     s1 := make([]int, 5)
 10     s2 := make([]int, 5, 10)
 11     s3 := make([]int, 10)[:5]
 12
 13     fmt.Printf("type: %T\n", s1)
 14     fmt.Println("len: ", len(s1))
 15     fmt.Println("cap: ", cap(s1))
 16     fmt.Println("----------")
 17     fmt.Printf("type: %T\n", s2)
 18     fmt.Println("len: ", len(s2))
 19     fmt.Println("cap: ", cap(s2))
 20     fmt.Println("----------")
 21     fmt.Printf("type: %T\n", s3)
 22     fmt.Println("len: ", len(s3))
 23     fmt.Println("cap: ", cap(s3))
 24 }

运行结果如下

 

 

访问

 

赋值

 

比较

1) slice 无法做比较,不能直接使用 == 来测试两个 slice 是否拥有相同的元素。标准库中有 bytes.Equal() 来比较两个字节 slice([]byte)。但对于其它类型的 slice ,需要自定义函数以实现比较。

  8 func main() {
  9     s1 := []int{1, 2, 3}
 10     s2 := []int{1, 2, 4}
 11     fmt.Println(s1 == s2)
 12 }

  编译报错,提示 slice 只能与 零值 nil 比较

invalid operation: s1 == s2 (slice can only be compared to nil)

原因有两个,一是 slice 的元素是非直接的,有可能 slice 可以包含它自身;二是底层数组元素有可能会改变,如果底层数组元素改变,同一个 slice 在不同的时间会拥有不同的元素。还需要在实践中去体会。

2) slice 唯一允许的比较操作是和 nil 做比较

示例代码如下

  8 func main() {
  9     s1 := []int{1, 2, 3}
 10     s2 := []int{1, 2, 4}
 11     var s3 []int
 12
 13     fmt.Println(nil == s1)
 14     fmt.Println(nil == s2)
 15     fmt.Println(nil == s3)
 16 }

  

运行结果如下

 

 

判空

检查一个 slice 是否为空,请使用 len(s) == 0,而不是 s == nil。在 s != nil 的情况下,slice 也有可能为空。

第9行声明了 slice 类型变量 s1,它是零值,可以通过第21行的打印结果可以知道。此时,它的长度为0。第15行声明了 slice 类型变量 s2,它是一个空的 slice,并非零值  nil,通过第22行的打印结果可知。此时,它的长度也为0。

所以在检查 slice 是否为空时,使用 0 == len(s) 更为合理。示例代码如下

  8 func main() {
  9     var s1 []int
 10     fmt.Println(s1)
 11     fmt.Println("len: ", len(s1))
 12     fmt.Println("cap: ", cap(s1))
 13
 14     fmt.Println("----------")
 15     s2 := []int{}
 16     fmt.Println(s2)
 17     fmt.Println("len: ", len(s2))
 18     fmt.Println("cap: ", cap(s2))
 19
 20     fmt.Println("----------")
 21     fmt.Println(s1 == nil)
 22     fmt.Println(s2 == nil)
 23 }

  

运行结果如下

 

 字节 slice 与字符串

对字节 slice([]byte) 做 slice 操作,求字符串子串操作,都写作 x[m:n],都返回原始字节的一个子序列,底层引用方式也相同。不同点在于,如果 x 是一字节 slice ,那么 x[m:n] 返回的结果是字节 slice;如果 x 是字符串,那么 x[m:n] 返回的是一个字符串。

 

 

 

 修改

 

 

 

使用

作函数参数

slice 包含了指向数组元素的指针,所以将一个 slice 传递给函数的时候,可以在函数内部修改底层数组的元素。即,创建一个数组的 slice 等于为数组创建了一个别名。

第10行代码,声明了一个 slice t,第20行代码,定义了一个逆序的函数,函数接收的参数是 slice 类型。slice t 中包含了指针属性,所以调用函数,可以直接对 slice t 进行修改。

第15行代码,声明了一个 整型数组 a,调用 reverse() 函数时,需要传入的是 slice 类型,实参写成了 a[:],表示对整个数组 a 的引用。 若实参直接写在 a,即 reverse(a),则会报错 "cannot use a (type [5]int) as type []int in argument ",实参类型与形参类型不一致。

  8 func main() {
  9     // slice t
 10     t := []int{1, 2, 3, 4, 5}
 11     reverse(t)
 12     fmt.Println(t)
 13
 14     // array a
 15     a := [...]int{1, 2, 3, 4, 5}
 16     reverse(a[:])
 17     fmt.Println(a)
 18 }
 19
 20 func reverse(s []int) {
 21     for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
 22         s[i], s[j] = s[j], s[i]
 23     }
 24 }

  

  运行结果如下

 

 增长

 

 

 

 

注意

越界

如果 slice 的引用超过了被引用对象的容量,即 cap(s),那么会导致程序宕机。如果 slice 的引用超出了被引用对象的长度,即 len(s),但没有超过被引用对象的容量,那么最终 slice 会比原 slice 长。

声明了两个 slice 变量 Q2 和 summer。slice Q2 引用了数组 months 中 4: "April", 5: "May", 6: "June" 三个元素,长度为3,容量为9。 slice summer 引用了数组 months 中 6: "June", 7: "July", 8: "August" 三个元素,长度为3,容量为7。

第28行代码,声明了一个 slice 变量 endlessSummer ,它引用了 slice 变量 summer,起始位置省略了,endlessSummer 的的起始位置默认即是 summer 的起始位置( 6: "June"),长度为 (5-0),容量为7,endlessSummer 它指向的最后一个元素是  "October"。

第35行代码,打印 从slice 变量 Q2 起始位置开始的 20 个元素,但是编译报错 , “slice 界限超出了容量范围9”。Q2 的起始位置指向原始数组中的元素 “April”,slice Q2 的容量为9,从 “April” 至结束共有9个元素 。打印20个元素,超出了容量。

  8 func main() {
  9     months := [...]string{1: "January", 2: "February", 3: "March",
 10         4: "April", 5: "May", 6: "June",
 11         7: "July", 8: "August", 9: "September",
 12         10: "October", 11: "November", 12: "December"}
 13
 14     Q2 := months[4:7]
 15     fmt.Println(Q2)
 16     fmt.Printf("type: %T\n", Q2)
 17     fmt.Println(len(Q2))
 18     fmt.Println(cap(Q2))
 19     fmt.Println("----------")
 20
 21     summer := months[6:9]
 22     fmt.Println(summer)
 23     fmt.Printf("type: %T\n", summer)
 24     fmt.Println(len(summer))
 25     fmt.Println(cap(summer))
 26     fmt.Println("----------")
 27
 28     endlessSummer := summer[:5]
 29     fmt.Println(endlessSummer)
 30     fmt.Printf("type: %T\n", endlessSummer)
 31     fmt.Println(len(endlessSummer))
 32     fmt.Println(cap(endlessSummer))
 33     fmt.Println("----------")
 34
 35     fmt.Println(Q2[:20])
 36 }

运行结果如

 

标签:slice,int,fmt,len,Println,数组,Go,程序设计
来源: https://www.cnblogs.com/zhe666/p/15706470.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有