TOC

Golang 数组 & 切片

数组声明时可以使用 ... 当作长度,表示自动判断。
数组声明时,可以只初始化其中的部分值。

a := [...]int{2: 1, 4: 2} // [5]int{0, 0, 1, 0, 4}

b := [5]int{1, 2, 3} // [5]int{1, 2, 3, 0, 0}

基本操作

a := [...]int{1, 2, 3}
var b []int

// 新增
b = append(b, 1)
// 删除元素
s = append(s[:index], s[index+1:]...)

// 遍历
for i := range a {
    fmt.Println(a[i])
}
for index, value := range a {
    fmt.Printf("%v: %v", index, value)
}
for _, value := range a {
    fmt.Printf("%v", value)
}
for i := 0; i < len(array); i++ {
    fmt.Printf("value: %d\n", array[i])
}

// 判断元素是否存在/获取元素序号
func Index(target int, array []int) int {
    for i, v := range array {
        if target == v {
            return i
        }
    }
    return -1
}

示例

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := [5]int{1, 3, 5, 7, 9}
    fmt.Printf("%s\t%#v\n", reflect.TypeOf(a), a)
    // [5]int   [5]int{1, 3, 5, 7, 9}

    // array => slice
    b := a[:] // a[0:len(a)]
    fmt.Printf("%s\t%#v\n", reflect.TypeOf(b), b)
    // []int    []int{1, 3, 5, 7, 9}

    // slice => array
    // c := ([5]int)(b) // cannot convert b (type []int) to type [5]int
    c := (*[5]int)(b) // 切片只能转换成数组指针
    fmt.Printf("%s\t%#v\n", reflect.TypeOf(c), c)
    // *[5]int  &[5]int{1, 3, 5, 7, 9}

    // 用类型别名试试:
    type NumArr [5]int
    c2 := (*NumArr)(b)
    fmt.Printf("%s\t%#v\n", reflect.TypeOf(c2), c2)
    // *main.NumArr &main.NumArr{1, 3, 5, 7, 9}

    // 只能遍历赋值
    d := [5]int{}
    for index, v := range b {
        d[index] = v
    }
    fmt.Printf("%s\t%#v\n", reflect.TypeOf(d), d)

    // 通过 copy 的方法实现 slice2array
    e := [5]int{}
    copy(e[:], b) // return length of b
    fmt.Printf("%s\t%#v\n", reflect.TypeOf(e), e)
}

去重

func Unique(arr []int) []int {
    arrLen := len(arr) - 1

    for arrLen > 0 {
        for i := arrLen - 1; i >= 0; i-- {
            if arr[arrLen] == arr[i] {
                arr = append(arr[:i], arr[i+1:]...)
                break
            }
        }
        arrLen--
    }

    return arr
}

func UniqueOptimized(arr []int) []int {
    uniqueArr := make([]int, 0, len(arr))
    uniqueMap := make(map[int]struct{})

    for _, num := range arr {
        if _, ok := uniqueMap[num]; !ok {
            uniqueArr = append(uniqueArr, num)
            uniqueMap[num] = struct{}{}
        }
    }

    return uniqueArr
}

// BenchmarkUniqueOriginal-20           135           8658964 ns/op               0 B/op          0 allocs/op
// BenchmarkUniqueOptimized-20         3501            347027 ns/op          285402 B/op        208 allocs/op

可以看到,一个性能好一些(只用 4% 的时间),资源使用多一些。

使用反射

https://blog.csdn.net/youngwhz1/article/details/83026263

func SliceRemoveDuplicate(a interface{}) (ret []interface{}) {
    if reflect.TypeOf(a).Kind() != reflect.Slice {
        fmt.Printf("<SliceRemoveDuplicate> <a> is not slice but %T\n", a)
        return ret
    }
    va := reflect.ValueOf(a)
    for i := 0; i < va.Len(); i++ {
        if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {
            continue
        }
        ret = append(ret, va.Index(i).Interface())
    }
    return ret
}

func SliceInsert(s []interface{}, index int, value interface{}) []interface{} {
    rear := append([]interface{}{}, s[index:]...)
    return append(append(s[:index], value), rear...)
}

func SliceInsert2(s *[]interface{}, index int, value interface{}) {
    rear := append([]interface{}{}, (*s)[index:]...)
    *s = append(append((*s)[:index], value), rear...)
}

func SliceInsert3(s interface{}, index int, value interface{}) bool {
    if ps, ok := s.(*[]string); ok {
        if val, ok := value.(string); ok {
            rear := append([]string{}, (*ps)[index:]...)
            *ps = append(append((*ps)[:index], val), rear...)
            return true
        }
    } else if ps, ok := s.(*[]int); ok {
        if val, ok := value.(int); ok {
            rear := append([]int{}, (*ps)[index:]...)
            *ps = append(append((*ps)[:index], val), rear...)
        }
    } else if ps, ok := s.(*[]float64); ok {
        if val, ok := value.(float64); ok {
            rear := append([]float64{}, (*ps)[index:]...)
            *ps = append(append((*ps)[:index], val), rear...)
        }
    } else {
        fmt.Printf("<SliceInsert3> Unsupported type: %T\n", s)
    }
    return false
}

func SliceRemove(s []interface{}, index int) []interface{} {
    return append(s[:index], s[index+1:]...)
}

func SliceRemove2(s *[]interface{}, index int) {
    *s = append((*s)[:index], (*s)[index+1:]...)
}

func SliceRemove3(s interface{}, index int) bool {
    if ps, ok := s.(*[]string); ok {
        *ps = append((*ps)[:index], (*ps)[index+1:]...)
    } else if ps, ok := s.(*[]int); ok {
        *ps = append((*ps)[:index], (*ps)[index+1:]...)
    } else if ps, ok := s.(*[]float64); ok {
        *ps = append((*ps)[:index], (*ps)[index+1:]...)
    } else {
        fmt.Printf("<SliceRemove3> Unsupported type: %T\n", s)
        return false
    }
    return true
}

func SliceClear(s *[]interface{}) {
    *s = append([]interface{}{})
}

func SliceClear2(s *[]interface{}) {
    *s = (*s)[0:0]
}

func SliceClear3(s interface{}) bool {
    if ps, ok := s.(*[]string); ok {
        *ps = (*ps)[0:0]
        //*ps = append([]string{})
    } else if ps, ok := s.(*[]int); ok {
        *ps = (*ps)[0:0]
        //*ps = append([]int{})
    } else if ps, ok := s.(*[]float64); ok {
        *ps = (*ps)[0:0]
        //*ps = append([]float64{})
    } else {
        fmt.Printf("<SliceClear3> Unsupported type: %T\n", s)
        return false
    }
    return true
}

泛型实现

func Insert[S ~[]E, E any](s S, i int, v ...E) S {
    tot := len(s) + len(v)
    if tot <= cap(s) {
        s2 := s[:tot]
        copy(s2[i+len(v):], s[i:])
        copy(s2[i:], v)
        return s2
    }
    s2 := make(S, tot)
    copy(s2, s[:i])
    copy(s2[i:], v)
    copy(s2[i+len(v):], s[i:])
    return s2
}

内置方法 make

make(Type, Len, Cap) 可以用来为 slice, map, channel 三种类型初始化(类似:C 语言 (int *)malloc(5))。

这里就看 Slice 的情况。

s1 := make([]int, 5)

s2 := make([]int, 5, 10)
// s2[8] = 1 // panic: runtime error: index out of range [8] with length 5
s2 = append(s2, 1)

如果有预留空间,append 的时候可以不用重新分配内存并遍历赋值。
如果切片有扩容的需要,就最好采用 make 来初始化。

如果不指定容量,则切片的容量和长度将相等。