go 切片

Created

2024-10-28 13:25:24

Updated

2024-10-28 13:25:29

1 数据结构

type SliceHeader struct {
    Data uintptr // 引用的数组的地址
    Len  int     // 切片长度
    Cap  int     // 引用的数组的长度
}

func TestSlice(t *testing.T) {
    // 方式1
    slice1 := []int{11, 22, 33, 44, 55}
    b := (*reflect.SliceHeader)(unsafe.Pointer(&slice1))
    fmt.Println(b.Cap, b.Len) // 5 5
    fmt.Println(len(slice1), cap(slice1))
    // 方式2
    arr := [5]int{11, 22, 33, 44, 55}
    slice2 := arr[1:3]
    c := (*reflect.SliceHeader)(unsafe.Pointer(&slice2))
    // 指向的数组是 arr 从1下标开始的. 所以cap 是4
    fmt.Println(c.Cap, c.Len) // 4 2
    // slice2[2]  越界了,len是可以访问的范围
    // 方式3
    slice3 := make([]int, 5)
    d := (*reflect.SliceHeader)(unsafe.Pointer(&slice3))
    // 5,5,824634322544   data 初始化了, 分配了内存,对应的字节数组元素都是0
    fmt.Println(d.Cap, d.Len, d.Data)
    slice33 := make([]int, 1, 5)
    dd := (*reflect.SliceHeader)(unsafe.Pointer(&slice33))
    // cap =5, len=1
    fmt.Println(dd.Cap, dd.Len, slice33[0])
    // 方式4
    var slice4 []int
    e := (*reflect.SliceHeader)(unsafe.Pointer(&slice4))
    // data 还没有初始化的 ,相比 slice4:=[]int{} 初始化推荐这种
    fmt.Println(e.Cap, e.Len, e.Data)

    a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    // 2个切片引用同一个底层数组
    a1 := a[1:4]
    a2 := a[2:5]
    a1[1] = 111
    fmt.Println(a) // 修改了底层数组元素
    a2[0] = 222
    fmt.Println(a, a1[1])
    a1 = append(a1, 333)
    fmt.Println(a) // 修改了底层数组元素

}

2 append

Tip
  • 由于真实的数据是通过引用数组,那么如果追加后长度超过了数组的cap,肯定需要重新分配一块内存,然后还需要将原来的数据复制到新的内存地址上
  • 新的内存要多大才好呢?
哪个版本的go 已经忘记了. 后续更新最新版本的
func growslice(et *_type, old slice, cap int) slice {
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        //如果期望容量大于当前容量的两倍就会使用期望容量
        newcap = cap
    } else {
        if old.len < 1024 {
            // 否则 如果当前切片的长度小于 1024 就会将容量设置为原先2倍大小
            newcap = doublecap
        } else {
            //如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
}

3 遍历 for range

func TestSlice(t *testing.T) {
    arr := []int{1, 2, 3}
    var v2 *int
    for _, v := range arr {
        if v == 2 {
            v2 = &v
        }
        fmt.Println(&v) // 同一个地址
    }
    fmt.Println(*v2) // 3
}

4 增加排序

package main

import (
    "fmt"
    "math/rand"
    "sort"
)

type Student struct {
    Name string
    Age  int
    Id   string
}
type StudentSlice []Student

// 注意我们是给切片 排序,所以是让切片实现了接口
func (p StudentSlice) Len() int {
    return len(p)
}
func (p StudentSlice) Less(i, j int) bool {
    return p[i].Name > p[j].Name
}
func (p StudentSlice) Swap(i, j int) {
    //这个彼此换,和python一样的写法
    p[i], p[j] = p[j], p[i]
}
func main() {
    var ss StudentSlice
    for i := 0; i < 10; i++ {
        s := Student{
            Name: fmt.Sprintf("stu%d", rand.Intn(100)),
            Id:   fmt.Sprintf("ID_%d", rand.Intn(100)),
            Age:  rand.Intn(100),
        }
        ss = append(ss, s)
    }
    for _, v := range ss {
        fmt.Println(v)
    }
    sort.Sort(ss)
    fmt.Println("=================")
    for _, v := range ss {
        fmt.Println(v)
    }
}
Back to top