Golang 切片和数组总结

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

最近起步学习golang,总结一下golang中切片和数组的关系和不同


1. 数组:

Go语言的数组不同于C语言或者其他语言的数组,C语言的数组变量是指向数组第一个元素的指针;而Go语言的数组是一个值,Go语言中的数组是值类型,一个数组变量就表示着整个数组,意味着Go语言的数组在传递的时候,传递的是原数组的拷贝。你可以理解为Go语言的数组是一种有序的struct
在golang中,数组的特点如下:

    1. 数组为值类型,换言之用数组给变量赋值,是将数组中所有元素拷贝一份
    2. 数组作为函数参数传递为值传递,而不是指针
    3. 数组的长度不可变,即不可进行扩容和缩容



数组的初始化:

//初始长度为5的数组
[5] int {1,2,3,4,5}
//用...代替初始长度,其长度是根据初始化时指定的元素个数决定的,本例中为5
[...] int {1,2,3,4,5}
//在初始化元素中可以指定特定下标的起始值,未指定的用0填充
//本例中数组为:[0,0,1,0,3]
[...] int {2:1,4:3}


2. 切片:

数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片(“动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象,但是这种情况也有一个例外,详见下面扩容部分。

其结构如下:

image
其数据结构分为3个部分:
    1. 地址指针:记录slice首个元素的地址
    2. 长度:当前slice中存了多少个数据
    3. 容量,当前slice中一共可以存多少数据(当容量不足的时候,会触发扩容机制)



切片的初始化:

//注意中括号中不可以有值,否则初始化的结果为数组
s := [] int {1,2,3 }

// : 的使用
s := arr[:] //arr的全部元素
s := arr[startIndex:endIndex] //arr的startIndex到endIndex-1
s := arr[startIndex:] //缺省endIndex时将表示一直到arr的最后一个元素
s := arr[:endIndex] //缺省startIndex时将表示从arr的第一个元素开始

s1 := s[startIndex:endIndex] //也可以用切片初始化切片

// 通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
s := make([]int,len,cap)

切片的引用机制

如下图image

当对slice或者array用冒号进行切片的时候,切片的长度会根据:左右的值进行自行运算,而容量为切片起始点到原数组/切片的结束结束点。

由于是引用类型,子切片值的改变也会影响到原数组/切片的值

import (
    "fmt"
)
func main(){
    arr := [4]int{10, 20, 30, 40}
    slice := arr[0:2]
    slice[0] = 1
    // 在触发扩容之前调用append,也会影响原数组的值
    slice = append(slice, 99)
    fmt.Println(slice)
    fmt.Println(arr)
}

程序结果:

[ 1 20 99 ]
[ 1 20 99 40 ]

切片的扩容机制
slice这种数据结构便于使用和管理数据集合,可以理解为是一种“动态数组”,slice也是围绕动态数组的概念来构建的。既然是动态数组,那么slice是如何扩容的呢?

请记住以下两条规则:

  • 如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。
  • 如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。

知道了一下规则,请看下面程序,试问输出结果:

import (
    "fmt"
)
func main(){
    arr := [4]int{10, 20, 30, 40}
    slice := arr[0:2]
    testSlice1 := slice
    testSlice2 := append(append(append(slice, 1),2),3)
    slice[0] = 11
  
    fmt.Println(testSlice1[0])
    fmt.Println(testSlice2[0])
}

由于append了3次而slice中的cap=4,则会触发扩容,因此testSlice2和slice并不是同一个slice,改变其中之一另一个不会收到影响

程序结果:

11
10
参考:
https://blog.csdn.net/belalds/article/details/80076739
https://i6448038.github.io/2018/08/11/array-and-slice-principle/
http://www.topgoer.com/

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

本文来自:Segmentfault

感谢作者:.container .card .information strong

查看原文:Golang 切片和数组总结

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

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