June's Studio.

Golang-数组与切片

字数统计: 723阅读时长: 3 min
2022/11/22

异同

slice 的底层数据是数组,slice 是对数组的封装,它描述一个数组的片段。两者都可以通过下标来访问单个元素。
组是定长的,长度定义好之后,不能再更改
而切片则非常灵活,它可以动态地扩容。切片的类型和长度无关
数组就是一片连续的内存, slice 实际上是一个结构体,包含三个字段:长度、容量、底层数组。

1
2
3
4
5
type slice struct{
array unsafe.Pointer //元素指针
len int //长度
cap int //容量
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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]

扩容

setp1: 预估容量
1.18之前:

当原 slice 容量小于 1024 的时候,新 slice 容量变成原来的 2 倍;原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。

1.18之后:

当原slice容量(oldcap)小于256的时候,新slice(newcap)容量为原来的2倍;原slice容量超过256,新slice容量newcap = oldcap+(oldcap+3*256)/4

step2: 预估大小 元素数量* 内存大小

step3: 根据内存管理模块匹配到合适的内存规格

切片a、b、c的长度和容量分别多少

1
2
3
4
5
6
func main(){
s:=[3]int{1,2,3}
a:=s[:0]
b:=s[:2]
c:=s[1:2:cap(s)]
}

答案:len(a)=0,cap(a)=3; len(b)=2,cap(b)=3; len(c)=1,cap(c)=2;

数组或切片的截取操作。截取操作有带 2 个或者 3 个参数,形如:[i:j] 和 [i:j:k],假设截取对象的底层数组长度为 l。
在操作符 [i:j] 中,如果 i 省略,默认 0,如果 j 省略,默认底层数组的长度,截取得到的切片长度和容量计算方法是 j-i、l-i。
操作符 [i:j:k],k 主要是用来限制切片的容量,但是不能大于数组的长度 l,截取得到的切片长度和容量计算方法是 j-i、k-i。

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
s := []int{5}
s = append(s, 7)
s = append(s, 9)
x := append(s, 11)
y := append(s, 12)
fmt.Println(s, x, y)
}

s:[5,7,9]
x: [5,7,9,12]
y:[5,7,9,12]

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
s := []int{1,2}
s = append(s,4,5,6)
fmt.Printf("len=%d, cap=%d",len(s),cap(s))
}

5,6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

func main() {
s := []int{1, 1, 1}
f(s)
fmt.Println(s)
}

func f(s []int) {
// i只是一个副本,不能改变s中元素的值
/*for _, i := range s {
i++
}
*/

for i := range s {
s[i] += 1
}
}

2 2 2
ps: Go 语言的函数参数传递,只有值传递,没有引用传递。

CATALOG
  1. 1. 异同
  • 扩容