go slice

go slice 特性

1、slice 并不是数组或数组指针。它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。
  • 引⽤类型。但⾃⾝是结构体,值拷⻉传递。
  • 属性 len 表⽰可⽤元素数量,读写操作不能超过该限制。
  • 属性 cap 表⽰最⼤扩张容量,不能超出数组限制。
  • 如果 slice == nil,那么 len、cap 结果都等于 0。


runtime.h

struct Slice
{                        // must not move anything
    byte*    array;      // actual data
    uintgo   len;        // number of elements
    uintgo   cap;        // allocated number of elements
};

在这里插入图片描述

len = high - low

cap = max - low


示例:

func test1() {
	data := [...]int {0,1,2,3,4,5,6,7,8,9}
	s1 := data[:6:8]		// 省略low
	s2 := data[5:]			// 省略high、max
	s3 := data[:3]			// 省略low、max
	s4 := data[:]			// 省略low、high、max,相当于引用整个数组

	fmt.Printf("the value of s1: %v, cap: %d, len: %d\n", s1, cap(s1), len(s1))
	fmt.Printf("the value of s2: %v, cap: %d, len: %d\n", s2, cap(s2), len(s2))
	fmt.Printf("the value of s3: %v, cap: %d, len: %d\n", s3, cap(s3), len(s3))
	fmt.Printf("the value of s4: %v, cap: %d, len: %d\n", s4, cap(s4), len(s4))
	
	
	s1[1] = 100
	fmt.Printf("the value of s1: %v, cap: %d, len: %d\n", s1, cap(s1), len(s1))
	fmt.Printf("the value of s4: %v, cap: %d, len: %d\n", s4, cap(s4), len(s4))
	
}

输出:

the value of s1: [0 1 2 3 4 5], cap: 8, len: 6
the value of s2: [5 6 7 8 9], cap: 5, len: 5
the value of s3: [0 1 2], cap: 10, len: 3
the value of s4: [0 1 2 3 4 5 6 7 8 9], cap: 10, len: 10
the value of s1: [0 100 2 3 4 5], cap: 8, len: 6
the value of s4: [0 100 2 3 4 5 6 7 8 9], cap: 10, len: 10

2、可直接创建 slice 对象,⾃动分配底层数组。

示例:

func test2() {
	s1 := []int {0, 1, 2, 3, 8:100}		// 通过初始化表达式构造,可使⽤索引号。与构造数组的区别就是没有指定长度。
	s2 := make([]int, 6, 8)				// 使⽤ make 创建,指定 len 和 cap 值。
	s3 := make([]int, 6)				// 省略 cap,相当于 cap = len。

	fmt.Printf("the value of s1: %v, cap: %d, len: %d\n", s1, cap(s1), len(s1))
	fmt.Printf("the value of s2: %v, cap: %d, len: %d\n", s2, cap(s2), len(s2))
	fmt.Printf("the value of s3: %v, cap: %d, len: %d\n", s3, cap(s3), len(s3))
	
	p := &s1[2]		// 可通过指针直接操作底层数组 
	*p += 100
	fmt.Printf("the value of s1: %v, cap: %d, len: %d\n", s1, cap(s1), len(s1))

}

输出:

the value of s1: [0 1 2 3 0 0 0 0 100], cap: 9, len: 9
the value of s2: [0 0 0 0 0 0], cap: 8, len: 6
the value of s3: [0 0 0 0 0 0], cap: 6, len: 6
the value of s1: [0 1 102 3 0 0 0 0 100], cap: 9, len: 9

使⽤ make 动态创建 slice,避免了数组必须⽤常量做⻓度的⿇烦。

3、切片的切片。

⾄于[][]T,是指元素类型为 []T

data := [][]int{
    []int{1, 2, 3},
    []int{100, 200},
    []int{11, 22, 33, 44},
}

4、reslice

所谓 reslice,是基于已有 slice 创建新 slice 对象,以便在 cap 允许范围内调整属性。

示例:

s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := s[2:5]        // [2 3 4]
s2 := s1[2:6:7]     // [4 5 6 7]
s3 := s2[3:6]       // Error

输出:

       +---+---+---+---+---+---+---+---+---+---+   
 data  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |   
       +---+---+---+---+---+---+---+---+---+---+   
          0         2               5
               +---+---+---+---+---+---+---+---+
 s1            | 2 | 3 | 4 |   |   |   |   |   |   len = 3, cap = 8
               +---+---+---+---+---+---+---+---+
                    0          2                   6    7
                       +---+---+---+---+---+
 s2                    | 4 | 5 | 6 | 7 |   |       len = 4, cap = 5
                       +---+---+---+---+---+
                              0               3    4    5
                                   +---+---+---+
 s3                                | 7 | 8 | X |   error: slice bounds out of range
								   +---+---+---+

新对象依旧指向原底层数组:

s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := s[2:5]       // [2 3 4]
s1[2] = 100
s2 := s1[2:6]      // [100 5 6 7]
s2[3] = 200
fmt.Println(s)	   // [0 1 2 3 100 5 6 200 8 9]

5、append

向 slice 尾部添加数据,返回新的 slice 对象

示例:

func test4() {
	s := make([]int, 1, 5)
	s1 := append(s, 100)
	s2 := append(s1, 200, 300)

	fmt.Printf("the value of s: %v, the address of s: %p\n", s, &s)
	fmt.Printf("the value of s1: %v, the address of s1: %p\n", s1, &s1)
	fmt.Printf("the value of s2: %v, the address of s2: %p\n", s2, &s2)
	fmt.Printf("the original array is: %v\n", *(*[5]int)(unsafe.Pointer(&s[0])))	// 为了打印原始数组,使用强制类型转换
}

输出:

the value of s: [0], the address of s: 0xc000004480
the value of s1: [0 100], the address of s1: 0xc0000044a0
the value of s2: [0 100 200 300], the address of s2: 0xc0000044c0
the original array is: [0 100 200 300 0]

简单点说,就是在 array[slice.high] 写数据

切片后添加另一个切片

示例:

func test8() {
	s1 := []int{1, 2, 3}
	s2 := []int{4, 5, 6}
	s3 := append(s1, s2...)
	s4 := append(s1[:1], s2...) //	将s2添加到s1[0]末尾
	s5 := append(s1, s2[:2]...) //	将s2[0]、s2[1]添加到s1末尾

	fmt.Printf("the value of s3: %v, the len: %d, the cap: %d\n", s3, len(s3), cap(s3))
	fmt.Printf("the value of s4: %v, the len: %d, the cap: %d\n", s4, len(s3), cap(s4))
	fmt.Printf("the value of s5: %v, the len: %d, the cap: %d\n", s5, len(s5), cap(s5))
}

输出:

the value of s3: [1 2 3 4 5 6], the len: 6
the value of s4: [1 4 5 6], the len: 4
the value of s5: [1 2 3 4 5], the len: 5


⼀旦超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。

示例:

func test5() {
	data := [...]int {0,1,2,3,4,5,6,7,8,9}
	s1 := data[:2:3]
	s2 := append(s1,100,200)
	fmt.Printf("the value of s1: %v, the address of s1[0]: %p\n", s1, &s1[0])
	fmt.Printf("the value of s2: %v, the address of s2[0]: %p\n", s2, &s2[0])
}

输出:

the value of s1: [0 1], the address of s1[0]: 0xc00000e140
the value of s2: [0 1 100 200], the address of s2[0]: 0xc00000a330

 从输出结果可以看出,append 后的 s2 重新分配了底层数组,并复制数据。如果只追加⼀个值,则不会超过 s1.cap 限制,也就不会重新分配。
 通常以 2 倍容量重新分配底层数组。在⼤批量添加数据时,建议⼀次性分配⾜够⼤的空间,以减少内存分配和数据复制开销。或初始化⾜够⻓的 len 属性,改⽤索引号进⾏操作。及时释放不再使⽤的 slice 对象,避免持有过期数组,造成 GC ⽆法回收


查看cap增长趋势:

s := make([]int, 0, 1)
c := cap(s)
for i := 0; i < 50; i++ {
    s = append(s, i)
    if n := cap(s); n > c {
        fmt.Printf("cap: %d -> %d\n", c, n)
        c = n
    }
}

输出:

cap: 1 -> 2
cap: 2 -> 4
cap: 4 -> 8
cap: 8 -> 16
cap: 16 -> 32
cap: 32 -> 64

6、copy

函数 copy 在两个 slice 间复制数据,复制⻓度以 len ⼩的为准。两个 slice 可指向同⼀底层数组,允许元素区间重叠。

示例:

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s := data[8:]
s2 := data[:5]
copy(s2, s)          // dst:s2, src:s
fmt.Println(s2)
fmt.Println(data)

输出:

[8 9 2 3 4]
[8 9 2 3 4 5 6 7 8 9]

copy(dst, src)

及时将所需数据 copy 到较⼩的 slice,以便释放超⼤号底层数组内存。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值