[Golang]也许有你不知道的,Array和Slice(2)

abv123456789 · · 4347 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。


Golang中的slice

1)基础

Slice更类似于"其他语言中的array",简单来说,它是一个指向一段数组的指针。

首先看看其声明:
  1. var intSlice []int  
上面声明了intSlice是一个指向int数组的slice,注意中括号里为空,这区别于array的声明;
另外这只是一个声明,所以intSlice会得到一个slice的默认值,即为nil:
  1. fmt.Printf("intSlice == nil? %v\n", intSlice == nil)  
  2. 输出:  
  3. intSlice == nil? true  
注意slice只能跟nil比较,如果你想尝试下面这代码:
  1. letsTry := intSlice  
  2. fmt.Printf("intSlice == letsTry? %v\n", intSlice == letsTry)  
你会得到下面这个错误信息:
  1. invalid operation: intSlice == letsTry (slice can only be compared to nil)  
接下来我们尝试创建一个数组并赋给intSlice:
  1. intSlice = make([]int, 1, 3)  
make()是builtin的方法,可以创建slice, map, chan,当然这里只讨论slice。
第一个参数是你要创建的东西的类型,这里要创建一个指向int数组的slice,即[]int;
第二个参数是该slice的长度,第三个参数是该slice的容量,这里分别是1和3;长度和容量分别代表什么,接下来我们会慢慢讲解。
我们先看一下我们刚才究竟创建了什么:
  1. fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))  
  2. 输出:  
  3. the intSlice is: [0], len: 1, cap: 3  
通过结果我们知道,刚才我们创建了一个长度为1(拥有一个元素)的数组,其元素的值为0(默认值);
所以,当我们尝试得到第二个元素,即intSlice[1]时:
  1. fmt.Printf("The intSlice[1] is: %d\n", intSlice[1])  
  2. 报错:  
  3. panic: runtime error: index out of range  
那当我们想向数组增加一个元素时怎么办?这就是cap的作用了。只要在cap的范围类,我们可以增加数组长度:
  1. intSlice = intSlice[:len(intSlice)+1]  
等号右面的代码返回一个新的slice,新的slice与intSlice指向同一个内存地址,但对内存里的array添加了一个默认元素,且长度+1;
将新的slice赋给intSlice,现在我们看看其内容:
  1. fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))  
  2. 输出  
  3. the intSlice is: [0 0], len: 2, cap: 3   
当然上面的操作看起来有点复杂,其实有更简单的表达,使用自带的append方法:
  1. intSlice = append(intSlice, 0)  
  2. fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))  
  3. 输出:  
  4. the intSlice is: [0 0 0], len: 3, cap: 3  
暂时还没问题;append()的用法在下一篇文章会具体说明。
那这数组长度可以无限增加吗?我们先舍弃append,继续使用一开始的扩展方式:
  1. intSlice = intSlice[:len(intSlice)+1]  
  2. fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))  
  3. 报错:  
  4. panic: runtime error: slice bounds out of range  
这里报错了,slice的len是不能超过其cap的。

在解决上面这个问题之前,先详细说明一下make([]int, 1, 3)究竟做了些什么。


2)make的细节

make([]int, 1, 3)究竟做了些什么?
首先,它在内存里分配了一段连续空间,这段空间的大小等于拥有3(cap)个元素的int数组(即[3]int{}),注意,只是空间大小相等;
然后,它在这段连续空间的开始位置,创建一个只有1(len)个元素的int数组,即[1]int{};

最后,它创建并返回一个slice,这个slice包含3个信息,指向的元素类型及内存位置(这里是刚才[1]int{}的第一个元素)、len(长度,这里为1),cap(容量,这里为3)


3)append的细节

好的,我们回到1)中最后的问题:如果一个slice的长度已达到其容量,而我想继续扩展,该怎么办呢?很简单,建立一个更大连续空间,并把原本slice的内容复制进去。

而其实builtin里已存在方法能智能帮我们完成这动作:就是刚才的append()。
  1. newIntSlice := append(intSlice, 0)  
  2. fmt.Printf("the newIntSlice is: %v, len: %d, cap: %d \n", newIntSlice, len(newIntSlice), cap(newIntSlice))  
  3. 输出:  
  4. the newIntSlice is: [0 0 0 0], len: 4, cap: 6  
我们可以看到newIntSlice的cap是原来的两倍。当len要超过cap时,append会帮我们创建一个容量是之前两倍的连续空间来存放元素
那newIntSlice中的前三个元素是从intSlice复制过来的吗(而不是用slice指向)?我们验证一下:
  1. newIntSlice[0] = 1  
  2. fmt.Printf("the newIntSlice is: %v, len: %d, cap: %d \n", newIntSlice, len(newIntSlice), cap(newIntSlice))  
  3. fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))  
  4. 输出:  
  5. the newIntSlice is: [1 0 0 0], len: 4, cap: 6  
  6. the intSlice is: [0 0 0], len: 3, cap: 3  
上面测试可以看出,修改newIntSlice不会影响intSlice。
当然intSlice与newIntSlice是相同类型的,可以直接用newIntSlice覆盖intSlice:
最后补充一下,创建slice的make方法可以只用两个参数,如make([]int, 3),这样得到的slice的len与cap都为3。

一下篇将会讨论slice的一些操作技巧

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

本文来自:CSDN博客

感谢作者:abv123456789

查看原文:[Golang]也许有你不知道的,Array和Slice(2)

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

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