GoLang之切片

本文探讨了Go语言中数组的局限性,并详细介绍了切片的定义、常用方法如append和copy,以及切片的len和cap属性。通过示例展示了切片在扩容时len和cap的变化规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GoLang之切片

一、数组的缺陷

我们在不使用切片的时候申请数组的时候,一般只能有一下的这些方式:

arr := [4]int{1,2,3,4}
arr2 := [4]int{}

从上面的代码可以看出使用数组的时候我们需要在定义的时候先将数组的大小进行确定,而且这一确定之后该数组的大小便无法改变了,这也就带来了数组的最大的一个问题就是容量无法扩容。当我们在使用数组的时候往往并不能知道这个数组最终会用到多大的空间,空间定义小了则会造成不够用,定义得过分大了则会造成空间的浪费。因此为了解决数组的这一个固有的问题,我们便引入了切片这一个类型。

二、切片的定义和几个常用的方法

append方法:

//1.在原有的数组上使用切片
arr := [4]int{11,22,33,44}
sli1 := arr[1:3]
fmt.Println(sli1)

//2.使用make函数进行定义
sli2 := make([]int,3,6) // 第二个参数定义的是切片的len,第三个参数定义的是切片的cap,第三个参数可以省略,默认和len保持一致
fmt.Println(sli2)

输出的结果:

[22 33]
[0 0 0]

我们要如何发挥切片长度可以自由扩容这一特性呢?就要使用切片的append方法:

arr := [4]int{11,22,33,44}
sli1 := arr[1:3]
fmt.Println(sli1)
sli1 = append(sli1, 55)
fmt.Println(sli1)
sli1 = append(sli1, 66)
fmt.Println(sli1)

输出结果:

[22 33]
[22 33 55]
[22 33 55 66]

可以看到我们使用append方法对切片进行元素的添加之后,不受切片原有的容量和长度的影响可以自由的进行添加。

copy方法:

arr := [4]int{11,22,33,44}
sli1 := arr[1:4]
sli2 := make([]int,len(sli1))
copy(sli2, sli1) //两个参数,第一个参数是目标切片,第二个参数是原切片
sli3 := sli1
fmt.Println(sli1,sli2,sli3) //[22 33 44] [22 33 44] [22 33 44]
sli1[0] = 1
sli3[1] = 2
fmt.Println(sli1,sli2,sli3) //[1 2 44] [22 33 44] [1 2 44]

从上面的代码可以看出,在使用copy函数和直接赋值两种对切片的操作中,看结果都是讲原来的切片的内容复制到一个新的切片之中,但是实际实现还是有区别的。

copy函数是值复制,可以理解为只是单纯的将内容从一个切片搬运到另外一个切片当中,两个切片之间没有其他的任何关系,改变其中一个切片的内容也不会对另外一个切片的内容造成影响。

而赋值的操作实质上是指针的复制,即创建一个新的指针指向同一个切片,相应的,如果改变某个指针指向的切片的值,另一个指针指向的内容也会相应的随之改变。

三、切片中的两个属性

len属性

len属性的意义就是当前切片的长度,可以简单的理解为当前切片中含有的元素的数量:

arr := [4]int{11,22,33,44}
sli1 := arr[1:3]
fmt.Println(len(sli1))
sli1 = append(sli1, 55)
fmt.Println(len(sli1))
sli1 = append(sli1, 66)
fmt.Println(len(sli1))

输出结果:

2
3
4

可以简单的理解为打印出来的切片中函数结果元素,他的len值就是多少。

cap属性

我们知道,切片的底层实际是数组,即从一个已有的数组中切一下来的一部分:

arr := [4]int{11,22,33,44}
sli1 := arr[1:3]
fmt.Println("原有的数组:",arr)
fmt.Println("切片:",sli1)
fmt.Println("当前切片的len值:", len(sli1))
fmt.Println("当前切片的cap值:", cap(sli1))

输出结果:

原有的数组: [11 22 33 44]
切片: [22 33]
当前切片的len值: 2
当前切片的cap值: 3

显而易见,当前的切片打印出来有两个元素,他的len值当然是2,但是为什么cap值是3呢?

arr := [4]int{11,22,33,44}
sli1 := arr[1:2]
fmt.Println("当前的切片",sli1)  //当前的切片 [22]
fmt.Println("当前切片的len值:", len(sli1)) //当前切片的len值: 1
fmt.Println("当前切片的cap值:", cap(sli1)) //当前切片的cap值: 3

sli2 := arr[1:3]
fmt.Println("当前的切片",sli2) //当前的切片 [22 33]
fmt.Println("当前切片的len值:", len(sli2)) //当前切片的len值: 2
fmt.Println("当前切片的cap值:", cap(sli2)) //当前切片的cap值: 3

sli3 := arr[2:3]
fmt.Println("当前的切片",sli3) //当前的切片 [33]
fmt.Println("当前切片的len值:", len(sli3)) //当前切片的len值: 1
fmt.Println("当前切片的cap值:", cap(sli3)) //当前切片的cap值: 2

通过观察上面几种情况的cap值,我们会发现cap值与切片的底层数组(arr)有关。

总结成一句话就是,当前切片的cap值就是切片切的底层数组的第一个元素到数组的最后一个元素的元素的数量。

例如上面的sli1,切片的开头就是arr[1],即元素22,那么他的cap值就是3(22,33,44)

例如上面的sli3,切片的开头就是arr[2],即元素33,那么他的cap值就是2(33,44)

即cap的取值只看切片切的第一个元素,,从第一个元素数到原有数组的最后一个元素的数量就是cap的值。

那么接下来就是在使用append方法,会对一个切片的len和cap值产生怎么样的印象。

arr := [4]int{11,22,33,44}
sli1 := arr[1:2]
fmt.Println(sli1) //[22]
fmt.Println("len值是:", len(sli1)) //len值是: 1
fmt.Println("cap值是:", cap(sli1)) //cap值是: 3
sli1 = append(sli1, 55)
fmt.Println(sli1) //[22 55]
fmt.Println("len值是:", len(sli1)) //len值是: 2
fmt.Println("cap值是:", cap(sli1)) //cap值是: 3
sli1 = append(sli1, 66)
fmt.Println(sli1) //[22 55 66]
fmt.Println("len值是:", len(sli1)) //len值是: 3
fmt.Println("cap值是:", cap(sli1)) //cap值是: 3
sli1 = append(sli1, 77)
fmt.Println(sli1) //[22 55 66 77]
fmt.Println("len值是:", len(sli1)) //len值是: 4
fmt.Println("cap值是:", cap(sli1)) //cap值是: 6

从上面的输出结果可以看出,在当前的len值小于cap值的情况下新增元素,只会增加len值,cap值不会改变。而当增加过后len值大于cap值,cap值就会变为之前cap值的两倍。

总结的更加通俗则是:

1.首先判断,如果新申请的容量大于两倍的旧的容量,最终容量就是新申请的容量

2.否则判断,如果旧的切片容量小于1024,则最终容量就是就容量的2倍

3.否则判断,如果旧的切片的容量大于1024,则最终容量从旧容量开始循环增加原来的1/4,直到最终容量大于新申请的容量

4.如果最终容量计算值溢出,则最终容量就是新申请的容量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值