天天看点

Go基础编程——切片slice

切片SIice

1、其 本 身 并 不 是 数 组 , 它 指 向底层数组

2、作 为 变 长 数 组 的 替代 方 案 , 可 以 关 联底层 数 组 的 局 部 或 全 部

3、为 引 用 类 型

4、可 以 直 接 创 建 或从底层 数 组 获 取 生 成

5、使 用 len() 获 取 元 个 数,cap() 获 取 容 量

6、一 般 使 用 make() 创 建

7、如 果 多 个Slice 指 向 相 同 底 叟 组 , 其 中 一 个 的 值 改 变 会 影 响 全 部

8、make(()T, Jen, cap)

其 中 ( ap 可 以 省 略 , 则和len 的 值 相 同

len 表 示 存储的 元素个 数 ,cap表示容量

一个Slice的令名如下

使用切片Slice截取数组的使用案例:

a :=[]int{,,,,,,,,,}
fmt.Println(a)
s:=a[:]
fmt.Println(s)
           

运行结果如下:

[         ]
[    ]
           

值得注意的是——[5:10]包含起始端,不包含终止端。也就是数学上常说的左闭右开。

其实还有两种方法可以实现把后面的数据全部截取

s:=a[:]
s:=a[:len(a)]
           

其实后一种方法等价于前一种,在比较好的编辑器上会提示

也就是说len(a)在这里属于多余的索引。

同样去前5个的方法:

使用make()函数声明Slice的方法

s1:=make([]int)
           

关于第一个参数3是初始化为3个int类型的数据,容量是10个,当超出10的时候,Go语言机制中,容量翻倍成20,当达到21个时超出20个了,容量会继续翻倍成为40个,这些都是Go语言底层的机制实现的。这里记住一点计算机重新分配内存地址是一种效率比较低的行为,尽量让容量一次满足条件。还有当我们不设置第三个参数10的时候,也是可以的,这时候默认的最大容量就是3个。

Slice与底层数组之间的关系

下边实现截取Slice_a的方法

a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
sa:=a[:]
fmt.Println(a)
fmt.Println(string(sa))
           

截取Slice_b方法如下:

a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
sb:=a[:]
fmt.Println(a)
fmt.Println(string(sb))
           

Reslice

1、Reslice 时 索 引 以 被Slice的 片 为 准

2、索 引 不 可 以 超 过 被 slice 的 切 片 的 容 量 cap() 值

3、索 引 越 界 不 会 导 致 底 层 数 组 的 重 新 分 配 而 是 引 发错误

Append(重点)

1、可 以 在Slice尾 部 追 加 元 素

2、可 以 将 一 个 slice 追加 在 另 一 个slice 尾 部

3、如 果 最 终 长 度 未 超 过 追 加 到 Slice 的 容 量 则 返 回 原 始Slice

4、如 果 超 过 追 加 到 的Slice的 容 量 则 将 重 新分配数组并拷贝原 始 数 据

Copy

以下程序实现Reslice:

a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
sa:=a[:]
sb:=sa[:]
fmt.Println(a)
fmt.Println(string(sa))
fmt.Println(string(sb))
           

string(sb)运行结果

依旧是de,这就是Reslice

这里引出一个之前对于Slice理解的一个误区,看下边程序以及运行结果:

运行结果是:

fg

这里会有些误解认为sa只包含cde三个元素,怎么会再次截取的时候会超出这三个呢,用程序来帮助理解是最好的办法。

fmt.Println(len(sa),cap(sa))
           

运行结果是

3 9
           

就知道它的容量是9个,长度是3个。只是sa包含cde三个而已,Slice指向的是个连续的内存块。

a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
sa:=a[:]
sb:=sa[:]
fmt.Println(a)
fmt.Println(len(sa),cap(sa))
fmt.Println(string(sa))
fmt.Println(string(sb))
           

运行结果:

就报错了,说超出容量了。由前边的cap(sa)我们知道,最大容量是9个,这里就要求把程序中的[3:11]改为[3:9]

接下来是这节的重点,使用append()函数在Slice后面追加元素

s1:=make([]int,,)
fmt.Printf("%v,%p\n",s1,s1)
s1=append(s1,,,,)
fmt.Printf("%v,%p\n",s1,s1)
           

运行结果

0xc042066030
[0 0 0 1 2 1 3],0xc04206e0a0
           

添加之后显示它的值和内存地址,可以看内存地址是不一样的,值不一样,这里的内存地址不一样的原因是,原来的最大容量5个,初始值的个数是3个在添加4个之后,最大容量翻倍变成10个。这时候的内存地址肯定不一样,因为Go语言的底层机制重新分配了一些内存给它。

想内存地址一样,

s1:=make([]int)
           

这时候的运行结果如下

[0 0 0],0xc04206e0a0
[0 0 0 1 2 1 3],0xc04206e0a0
           

内存地址变成一样了。

接下来说一个重点,当两个切片共享同一个元素(共同指向同一个元素)时,只要其中一个切片改变这个元素,另外一个切片也会随着改变。

看如下程序以及运行结果:

a:=[]int{,,,,,,}

s1:=a[:]
s2:=a[:]
   fmt.Println(a,s1,s2)
s1[]=
fmt.Println(a,s1,s2)
           

运行结果

[      ] [  ] [  ]
[      ] [  ] [  ]
           

当然也把原始的数组改变了。这里还有要注意的地方就是使用append改变s2时,当append()后超出原有的容量,Go语言的底层机制会给s2重新分配内存地址,它就不是原来的地址,这时候s1[0]=9对于s2来说是不影响的。看程序如下,以及它的运行结果就可以解释这个了,这是我们使用append时特别注意的地方。

a:=[]int{,,,,,,}

s1:=a[:]
s2:=a[:]
   fmt.Println(a,s1,s2)
   s2=append(s2,,,,,,,,,,,,)
s1[]=
fmt.Println(a,s1,s2)
           

运行结果如下

[      ] [  ] [  ]
[      ] [  ] [              ]
           

可以看到原来s2的3没有被改变,这里s2的3已经不是和s1指向同一块地址了,所以没法改变它,用程序输出s1和s2指向的内存看看就知道了。

fmt.Printf("%p,%p\n",s1,s2)
           

运行结果如下

0xc042070050,0xc042072080
           

最后谈谈数组的copy函数

s1:=[]int{,,,,,}
s2:=[]int{,,}
copy(s1,s2)
fmt.Println(s1)
           

运行结果

[     ]
           

反过来s1复制到s2中

copy(s2,s1)
fmt.Println(s2)
           

运行结果是

[  ]
           

容量长的切片复制到容量短的切片是,以容量短的为准,换句话说以容器为准。

再说说值复制一部分是如何实现的?

假如只复制s1中的4,5两个元素到s2中

copy(s2,s1[:])
fmt.Println(s2)
           

运行结果

[  ]
           

同样复制的时候在容器中默认从下标为0的位置开始放置复制过来的元素,当然也可以制定它放置的具体位置,比如上例中的复制元素4,5我想把它们放置在第二个和第三个位置,操作如下:

copy(s2[1:3],s1[3:5])
           

运行结果:

[  ]
           

这样就实现了想要的。

思考以下问题:

如何将一个Slice指向一个完整的底层数组,而不是部分底层数组?

接下来我就是来实现它:

s1:=[]int{,,,,,}
s2:=[]int{,,}
      copy(s2[:],s1[:])
      s3:=s1[:]
fmt.Println(s2)
fmt.Println(s3)
           

其中s3就实现了上述题目的要求。

当然也可以使用

s3:=s1[:]
s3:=s1[:]
s3:=s1[:]
           

这三种方式完成。