当前位置: 首页 > 后端技术 > PHP

GO切片的理解

时间:2023-03-30 00:47:16 PHP

切片的理解GO中的数组是定长数据结构,切片可以理解为动态数组的概念。它以数组为基础,提供动态扩展的API。在使用上可以理解为Java中的一个ArrayList,但是它的底层还是有很大区别的。切片的组成切片主要包含三部分:指向底层数组的指针(pointer)、容量(capacity)、长度(length)。从组成上我们可以看出,切片本身并不包含数组,而是有一个指向底层数组的指针。这和Java中的ArrayList是不同的。因为每个ArrayList都有一个“指针”指向自己唯一的数组,而对于GO切片来说,可能有多个切片对应同一个底层数组。slice的基本原理定义一个slice的代码如下:slice:=make([]string,2)fmt.Println("capacity:",cap(slice),"length:",len(slice))//输出:容量:2长度:2slice=make([]string,2,3)fmt.Println("容量:",cap(slice),"长度:",len(slice))//输出:容量:3Length:2复制代码可以看到,如果不指定容量,默认容量和长度一样。那么执行make之后,内部数据结构是怎样的呢?以slice=make([]string,2,3)为例:如图,底层会创建一个长度为切片容量的数组,并将切片的指针指向第一个元素大批。根据切割边缘的长度限制访问。此时slice的长度为2,如果此时访问slice的第三个元素,会报错:println(slice[2])//runerror:panic:runtimeerror:indexoutofrange[2]和长度为2的复制代码slice的英文单词是slice,这个名字是有意义的,我们可以从一个slice中“切”出一个新的slice。这个操作看起来像这样:newSlice:=slice[1:2:3]fmt.Println("Capacity:",cap(newSlice),"Length:",len(newSlice))//output:capacity:2length:1复制代码此时底层结构如下:根据上图,让我们理解slice[1:2:3]后面三个数字的含义:第一个下标,这里是1:指的是新的切片从原切片指向的数组的索引开始,它指定了新切片的起始下标;第二个下标,这里是2:指的是新切片的长度在原数组中的位置,这里指定为2(不是数组索引),起始位置是1,所以新切片的长度是2-1=1;第三个下标:这里是3:指的是新切片的容量在原数组中的位置,这里指定为3(不是数组索引),起始位置为1,所以新切片的容量是3-1=2;这里要注意,如果指定的三个下标的值超过了原底层数组(不是索引)的长度,就会报Arrayoutofbounds错误。此时,两个切片共享一个底层数组,任何对其中一个切片的元素的修改都会相互影响。slice:=make([]string,2)slice=make([]string,2,3)newSlice:=slice[1:2:3]newSlice[0]="张三"printSlice(slice,"slice")printSlice(newSlice,"newSlice")funcprintSlice(slice[]string,namestring){fmt.Println("-----开始打印"+name+"sliceelement")forindex,item:=rangeslice{fmt.Println(name+"indexis",index,"dataatposition:"+item)}fmt.Println(name,"length:",len(slice),"capacity:",cap(slice))fmt.Println("-----结束打印"+name+"切片元素")}//-----开始打印切片的切片元素//切片索引0位置的数据为://slice索引为1的位置的数据为:张三//-----结束打印slice的切片元素//-----开始打印newSlice的切片元素//The0位置的数据newSlice索引是:张三//-----结束打印newSlice的切片元素复制代码slice和append函数我们定义好切片后,需要添加元素,需要用到append方法。slice:=[]string{"茄子","土豆","黄瓜","西瓜"}newSlice:=slice[1:3:4]printSlice(slice,"slice")printSlice(newSlice,"newSlice")//-----开始打印切片的切片元素//切片索引0处的数据为:茄子//切片索引1处的数据为:土豆//切片索引2处的数据为:cucumber//切片索引为3,位置数据为:西瓜//切片长度:4容量:4//-----结束打印切片的切片元素//-----开始打印切片的切片元素newSlice//newSlice的0位置的数据是:potatoes//newSlice的1位置的数据是:cucumber//newSlice的长度是:2容量是:3//-----结束打印切片elementsofnewSlice在此时复制代码底层数组的结构如图所示,其中slice的长度和容量都是4,也就是整个底层数组,newSlice指定slice[1:3:4],即自身容量为3,但此时分片T长度为2,此时我们执行第一次数据添加:newSlice=append(newSlice,"banana")printSlice(slice,"第一次添加数据后:slice")printSlice(newSlice,"添加数据后forthefirsttime:newSlice")//-----第一次添加数据后开始打印:切片的切片元素//第一次添加数据后:切片索引所在位置的数据0:eggplant//第一次添加数据后:切片索引1处的数据为:potatoes//第一次添加数据后:切片索引2处的数据为:cucumber//第一次添加数据后:theslice索引3处的数据为:bananas//第一次添加数据后:slicelength:4capacity:4//-----结束打印第一次添加数据后:slice的slice元素//-----开始打印第一次添加Afterthedata:sliceelementofnewSlice//第一次添加数据后:newSlice索引为0,位置数据为:potato//添加数据f后orfirsttime:newSliceindexis1,thedataatpositionis:cucumber//第一次添加数据后:newSliceindexis2位置数据为:Banana//第一次添加数据后:newSliceLength:3Capacity:3//-----结束打印第一次添加数据后:newSlice的slice元素复制代码这里可以看到,向newSlice添加数据后,slice的数据也会同时被修改。这是因为底层数据是共享的。长度为2,还有空间,所以数据'banana'会直接覆盖原来的'watermelon',因为对于newSlice来说,'watermelon'位置的元素对他来说是没有用的。这时候我们会第二次向newSlice中添加数据。newSlice=append(newSlice,"Apple")printSlice(slice,"第二次添加数据后:slice")printSlice(newSlice,"第二次添加数据后:newSlice")//-----开始第二次加数据后打印:切片的切片元素//第二次加数据后:切片索引为0的位置的数据为:eggplant//第二次加数据后:切片索引处的数据1is:potatos//第二次添加数据后:sliceindex2处的数据为:cucumber//第二次添加数据后:sliceindex3处的数据为:bananas//第二次添加数据后:slicelength:4capacity:4//-----第二次添加数据后结束打印:slice的slice元素//-----开始打印第二次添加数据After:newSlice的slice元素//添加数据后forthesecondtime:newSliceindexis0,thedataatpositionis:potato//第二次添加数据后:newSliceindexis1,thedataatposition//第二次添加Afterdata:newSlicewithindex2,dataatposition:banana//第二次添加数据后:newSlicewithindex3,dataatposition:Apple//第二次添加数据后:lengthofnewSlice:4Capacity:6//-----结束打印第二次添加数据后:newSlice的slice元素复制代码此时可以看到newSlice的容量直接翻倍了,而slice的容量没有改变!怎么回事,看下图:newSlice在append时,因为它的长度和容量是相等的,也就是它自己的元素已经很慢了。这时候底层数组已经没有空间可以添加元素了。这时候会创建一个newSlice。底层双倍容量的数组,将原来的数据复制一次放入新创建的数组中,然后将数据'apple'放入新添加的数组中,新的切片会复制数组的A指针到第一个元素新创建的数组。总结对于切片来说,它是一个不可变的数据结构。数组赋值是一个操作。底层数组对切片进行append操作,每次都会创建新的slice对象,所以每次append后,都必须对局部变量重新赋值和扩容,每次容量小于1000时,容量增加一倍,每次增加1.25倍时间过了1000多。最后,如果您觉得这篇文章对您有帮助,请点个赞。或者可以加入我的开发交流群:1025263163互相学习,我们会有专业的技术解答。如果您觉得这篇文章对您有用,请给我们的开源项目一个小星星:http://github。crmeb.net/u/defu非常感谢!PHP学习手册:https://doc.crmeb.com技术交流论坛:https://q.crmeb.com