Go解密之slice

转载地址

本文的灵感来自于某篇公众号文章:https://mp.weixin.qq.com/s?__biz=MjM5MDUwNTQwMQ==&mid=2257483743&idx=1&sn=af5059b90933bef5a7c9d491509d56d9&scene=21#wechat_redirect

什么是slice

slice 翻译成中文就是切片,它和数组(array)很类似,可以用下标的方式进行访问,如果越界,就会产生 panic。但是它比数组更灵活,可以自动地进行扩容

slice的创建

在这里插入图片描述
在这里插入图片描述

通过字面量创建的一个特殊形式:索引

截取也是比较常见的一种创建 slice 的方法,可以从数组或者 slice 直接截取,当然需要指定起止索引位置。

基于已有 slice 创建新 slice 对象,被称为 reslice。新 slice 和老 slice 共用底层数组,新老 slice 对底层数组的更改都会影响到彼此。基于数组创建的新 slice 对象也是同样的效果:对数组或 slice 元素作的更改都会影响到彼此。

值得注意的是,新老 slice 或者新 slice 老数组互相影响的前提是两者共用底层数组,如果因为执行 append 操作使得新 slice 底层数组扩容,移动到了新的位置,两者就不会相互影响了。所以,问题的关键在于两者是否会共用底层数组。

下面代码会输出什么?


package main
import "fmt"
func main() {
    s1 := []int{0, 1, 2, 3, 8:100}
    fmt.Println(s1, len(s1), cap(s1))
	s2 := []int{5:128}
    fmt.Println(s2, len(s2), cap(s2))
}

答案是:

[0 1 2 3 0 0 0 0 100] 9 9 //下标为8的元素是100
[0 0 0 0 0 128] 6 6 //下标为5的元素是128

上面的代码例子中使用了索引号,直接赋值,这样,其他未注明的元素则默认 0 值。

切片的截取与长度、容量的关系

下面代码输出什么?


package main

import "fmt"

func main() {
    slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    s1 := slice[2:5]
    s2 := s1[2:6:7]

    s2 = append(s2, 100)
    s2 = append(s2, 200)

    s1[2] = 20

    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(slice)
}

答案如下:

[2 3 20]
[4 5 6 7 100 200]
[0 1 2 3 20 5 6 7 100 9]

解决这个问题的关键先看切片初始的长度和容量

func main() {
	slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    s1 := slice[2:5]
    s2 := s1[2:6:7]
    fmt.Printf("slice的长度=%d,容量=%d \n",len(slice),cap(slice))//slice的长度=10,容量=10
    fmt.Printf("s1的长度=%d,容量=%d \n",len(s1),cap(s1))//s1的长度=3,容量=8
    fmt.Printf("s2的长度=%d,容量=%d \n",len(s2),cap(s2))//s2的长度=4,容量=5
}

s1的长度是slice切片下标2到下标4的元素的长度,为3;容量是下标2到结尾,为8。
s2的长度是s1切片下标2到下标5的元素的长度,为4;容量从下标2到索引7(开区间,真正到索引6),为5。

这两个切片都共用同一个底层数组。
在这里插入图片描述
接着,向 s2 尾部追加一个元素 100:s2 = append(s2, 100)
s2 容量刚好够,直接追加。不过,这会修改原始数组对应位置的元素。这一改动,数组和 s1 都可以看得到。
在这里插入图片描述
再次向 s2 追加元素200:s2 = append(s2, 200)
这时,s2 的容量不够用,该扩容了。于是,s2 另起炉灶,将原来的元素复制新的位置,扩大自己的容量。并且为了应对未来可能的 append 带来的再一次扩容,s2 会在此次扩容的时候多留一些 buffer,将新的容量将扩大为原始容量的2倍,也就是10了。

在这里插入图片描述
最后,修改 s1 索引为2位置的元素:s1[2] = 20

这次只会影响原始数组相应位置的元素。它影响不到 s2 了,人家已经远走高飞了。

在这里插入图片描述
再提一点,打印 s1 的时候,只会打印出 s1 长度以内的元素。所以,只会打印出3个元素,虽然它的底层数组不止3个元素。

总结:
•切片是对底层数组的一个抽象,描述了它的一个片段。
•切片实际上是一个结构体,它有三个字段:长度,容量,底层数据的地址。
•多个切片可能共享同一个底层数组,这种情况下,对其中一个切片或者底层数组的更改,会影响到其他切片。
•append 函数会在切片容量不够的情况下,调用 growslice 函数获取所需要的内存,这称为扩容,扩容会改变元素原来的位置。
•扩容策略并不是简单的扩为原切片容量的 2 倍或 1.25 倍,还有内存对齐的操作。扩容后的容量 >= 原容量的 2 倍或 1.25 倍。
•当直接用切片作为函数参数时,可以改变切片的元素,不能改变切片本身;想要改变切片本身,可以将改变后的切片返回,函数调用者接收改变后的切片或者将切片指针作为函数参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值