加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Golang 语言中的 Slice

发布时间:2020-12-16 18:38:51 所属栏目:大数据 来源:网络整理
导读:概念 Slice切片是对底层数组Array的封装,在内存中的存储本质就是数组,体现为连续的内存块,Go语言中的数组定义之后,长度就已经固定了,在使用过程中并不能改变其长度,而Slice就可以看做一个长度可变的数组进行使用,最为关键的,是数组在使用的过程中都

概念


Slice切片是对底层数组Array的封装,在内存中的存储本质就是数组,体现为连续的内存块,Go语言中的数组定义之后,长度就已经固定了,在使用过程中并不能改变其长度,而Slice就可以看做一个长度可变的数组进行使用,最为关键的,是数组在使用的过程中都是值传递,将一个数组赋值给一个新变量或作为方法参数传递时,是将源数组在内存中完全复制了一份,而不是引用源数组在内存中的地址,为了满足内存空间的复用和数组元素的值的一致性的应用需求,Slice出现了,每个Slice都是都源数组在内存中的地址的一个引用,源数组可以衍生出多个Slice,Slice也可以继续衍生Slice,而内存中,始终只有源数组,当然,也有例外,后边再说。


用法

1.Slice的定义

Slice可以通过两种方式定义,一种是从源数组中衍生,一种是通过make函数定义,本质上来说都一样,都是在内存中通过数组的初始化的方式开辟一块内存,将其划分为若干个小块用来存储数组元素,然后Slice就去引用整个或者局部数组元素。

从数组中切片构建Slice:

a:=[10]int{1,2,3,4,5,6,7,8,9,0}
s:=a[2:8]
fmt.Println(s)	//输出:[345678]

定义一个数组a,截取下标为2到8之间部分(包括2不包括8),构建一个Slice。

通过make函数定义:

s:=make([]int,10,20)
fmt.Println(s)//输出:[0000000000]
//make函数第一个参数表示构建的数组的类型,第二个参数为数组的长度,第三个参数可选,是slice的容量,默认为第二个参数值

2.Slice的长度和容量


Slice有两个比较混淆的概念,就是长度和容量,何谓长度?这个长度跟数组的长度是一个概念,即在内存中进行了初始化实际存在的元素的个数。何谓容量?如果通过make函数创建Slice的时候指定了容量参数,那内存管理器会根据指定的容量的值先划分一块内存空间,然后才在其中存放有数组元素,多余部分处于空闲状态,在Slice上追加元素的时候,首先会放到这块空闲的内存中,如果添加的参数个数超过了容量值,内存管理器会重新划分一块容量值为原容量值*2大小的内存空间,依次类推。这个机制的好处在能够提升运算性能,因为内存的重新划分会降低性能。

a:=[10]int{1,0}
s:=a[0:]
s=append(s,11,22,33)
sa:=a[2:7]
sb:=sa[3:5]
fmt.Println(a,len(a),cap(a))//输出:[1234567890]1010
fmt.Println(s,len(s),cap(s))//输出:[1234567890112233]1320
fmt.Println(sa,len(sa),cap(sa))//输出:[34567]58
fmt.Println(sb,len(sb),cap(sb))//输出:[67]25

//可以看出,数组的len和cap是永远相等的,并且是在定义的时候就已经指定的,不能改变。切片s引用这个数组的全部元素,初始长度和容量都为10,继续追加3个元素后,其长度变为13容量为20,。切片sa截取下标2到7的数组片段,长度为5,容量为8,这个容量的改变规则为原容量值减掉起始下标,此时若追加元素,会覆盖掉原内存地址中存在的值。切片sb截取切片sa下标3到5的数组片段,注意,这里的下标指的是sa的下标,不是源数组的下标,长度为2,容量为8-3=5。

3.Slice是引用类型

上边已经提到过,Slice是对源数组的一个引用,改变Slice中的元素的值,实质上就是改变源数组的元素的值。

a:=[10]int{1,0}
sa:=a[2:7]
sa=append(sa,100)
sb:=sa[3:8]
sb[0]=99
fmt.Println(a)//输出:[1234599710090]
fmt.Println(sa)//输出:[345997100]
fmt.Println(sb)//输出:[99710090]
//可以看到,不管是append操作,还是赋值操作,都影响了源数组或者其他引用同一数组的Slice的元素。Slice进行数组引用的时候,其实是将指针指向了内存中具体元素的地址,如数组的内存地址,事实上是数组中第一个元素的内存地址,Slice也是如此。
a:=[10]int{1,0}
sa:=a[2:7]
sb:=sa[3:8]
fmt.Printf("%pn",sa)		//输出:0xc084004290
fmt.Println(&a[2],&sa[0])//输出:0xc0840042900xc084004290
fmt.Printf("%pn",sb)		//输出:0xc0840042a8
fmt.Println(&a[5],&sb[0])//输出:0xc0840042a80xc0840042a8

4.Slice引用传递发生“意外”


上边我们一直在说,Slice是引用类型,指向的都是内存中的同一块内存,不过在实际应用中,有的时候却会发生“意外”,这种情况只有在像切片append元素的时候出现,Slice的处理机制是这样的,当Slice的容量还有空闲的时候,append进来的元素会直接使用空闲的容量空间,但是一旦append进来的元素个数超过了原来指定容量值的时候,内存管理器就是重新开辟一个更大的内存空间,用于存储多出来的元素,并且会将原来的元素复制一份,放到这块新开辟的内存空间。

a:=[]int{1,4}
sa:=a[1:3]
fmt.Printf("%pn",sa)//输出:0xc0840046e0
sa=append(sa,33)
fmt.Printf("%pn",sa)//输出:0xc084003200
//可以看到执行了append操作后,内存地址发生了变化,说明已经不是引用传递。

总之,slice是封装过的array,slice用起来真爽,不需要像c语言那样 超过数组size,remalloc size后copy

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读