slice 名为切片,是 Go 中的可变长数组,是对底层数组的封装和引用。切片指向一个底层数组,并且包含长度和容量信息。未初始化切片的值为 nil。作用于切片的内建函数主要有四个,分别是 make、len、cap 和 append。make 用于创建切片,len 用于获取切片的长度,cap 用于获取切片的容量,append 用于向切片追加元素。
package main
import "fmt"
func main() {
//创建切片,make([]T, length, capacity)
fib := make([]int, 0, 10)
fmt.Println("len(fib)=", len(fib))
fmt.Println("cap(fib)=", cap(fib))
fib = append(fib, []int{1, 1, 2, 3, 5, 8, 13}...)
fmt.Println("fib=", fib)
}
输出结果:
len(fib)= 0
cap(fib)= 10
fib= [1 1 2 3 5 8 13]
存放任意类型元素的切片可以使用 []interface{} 表示,但不能表示任意切片类型,即具体类型的切片无法转换为 []interface{} ,需要显示转换。
//@param:slice: 存放任何类型元素的切片(不是任意切片类型);index:待插入的下标;value:待插入的元素
//@ret: 结果切片
func insert(slice []interface{}, index int, value interface{}) []interface{} {
if index > len(slice) {
return slice
}
//尾部追加元素,使用append函数
if index == len(slice) {
return append(slice, value)
}
sl := append(slice[:index+1], slice[index:]...)
sl[index] = value
return sl
}
func main() {
fib := make([]interface{}, 0, 10)
fib = insert(&fib, 0, 1) //切片头部插入元素
fmt.Println("fib =",fib)
}
输出结果:
fib = [1]
注意,[]interface{} 表示存放任意类型元素的切片,并不是任意切片类型,所以使用具体类型的切片时,编译时将出现类型转换错误。比如 []int 切片不能赋值给 []interface{}。
fib := []int{1, 1}
r := insert(fib, 2, 2)
编译错误:
cannot convert fib (type []int) to type []interface {}
如果想对具体类型的切片进行插入,需要显示地将具体类型的切片转换为 []interface{},然后再将结果 []interface{} 切片转换回具体类型的切片。
func main() {
fib := []int{1}
r := make([]interface{}, len(fib))
for i, v := range fib {
r[i] = v
}
// 切片头部插入元素
o := InsertSlice(r, 1, 1)
fibR := []int{}
for _, v := range o {
fibR = append(fibR, v.(int))
}
fmt.Println("fib =", fibR)
}
输出结果:
fib = [1 1]
使用 []interface{} 实现,需要的将具体类型的切片转换为 []interface{},再将结果 []interface{} 切片转换为具体类型的切片,使用上非常繁琐。基于 []interface{} 的实现,可以进行进一步的封装。我们使用 interface{} 接收任意切片类型,利用 Go 提供的反射功能,实现对任意切片类型的插入操作。主要有如下步骤:
(1)将任意切片类型转换为[]interface{}
;
(2)对[]interface{}
进行插入;
(3)将结果[]interface{}
切片转换为具体类型的切片。
// CreateAnyTypeSlice 将任意切片类型转换为 []interface{}
func CreateAnyTypeSlice(slice interface{}) []interface{} {
//判断是否是切片类型
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return nil
}
sliceLen := v.Len()
out := make([]interface{}, sliceLen)
for i := 0; i < sliceLen; i++ {
out[i] = v.Index(i).Interface()
}
return out
}
// Insert 任意切片类型插入元素
func Insert(slice interface{}, index int, value interface{}) interface{} {
o := CreateAnyTypeSlice(slice)
if o == nil {
return nil
}
o = InsertSlice(o, index, value)
// 枚举不同的类型,暂未找到泛型的办法
switch value.(type) {
case int:
r := []int{}
for _, v := range o {
r = append(r, v.(int))
}
return r
case string:
r := []string{}
for _, v := range o {
r = append(r, v.(string))
}
return r
}
return nil
}
func main() {
fib := []int{1}
// 切片头部插入元素
o := Insert(fib, 1, 1)
if v, ok := o.([]int); ok {
fmt.Println("fib =", v)
}
}
编译运行输出:
fib = [1 1]
基于 []interface{} 的实现,仍然无法摆脱类型枚举的冗余做法,实际上可以摆脱 []interface{},借助 Golang reflect 包提供的反射功能,完全使用 interface{} 来实现。
// InsertSliceE insert a element to slice in the specified index
// Note that original slice will not be modified
func InsertSliceE(slice interface{}, index int, value interface{}) (interface{}, error) {
// check params
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return nil, errors.New("target isn't a slice")
}
if index < 0 || index > v.Len() || reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
return nil, errors.New("param is invalid")
}
dst := reflect.MakeSlice(reflect.TypeOf(slice), 0, 0)
// add the element to the end of slice
if index == v.Len() {
dst = reflect.AppendSlice(dst, v.Slice(0, v.Len()))
dst = reflect.Append(dst, reflect.ValueOf(value))
return dst.Interface(), nil
}
dst = reflect.AppendSlice(dst, v.Slice(0, index+1))
dst = reflect.AppendSlice(dst, v.Slice(index, v.Len()))
dst.Index(index).Set(reflect.ValueOf(value))
return dst.Interface(), nil
}
func main() {
fib := []int{1, 2, 3, 5, 8}
// 切片头部插入元素
o, _ := InsertSliceE(fib, 0, 1)
fmt.Println("fib =", o)
}
编译运行输出:
fib = [1 1 2 3 5 8]
// DeleteSliceE deletes the specified index element from the slice with error.
// Note that the original slice will not be modified.
func DeleteSliceE(slice interface{}, indexes ...int) (interface{}, error) {
// check params
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return nil, errors.New("the input isn't a slice")
}
if v.Len() == 0 || len(indexes) == 0 {
return slice, nil
}
// convert the indexes to map set
m := make(map[int]struct{})
for _, i := range indexes {
m[i] = struct{}{}
}
// delete
t := reflect.MakeSlice(reflect.TypeOf(slice), 0, v.Len())
for i := 0; i < v.Len(); i++ {
if _, ok := m[i]; !ok {
t = reflect.Append(t, v.Index(i))
}
}
return t.Interface(), nil
}
func main() {
fib := []int{1, 1, 2, 3, 5, 8}
o, _ := DeleteSliceE(fib, len(fib) - 1) //删除切片最后一个元素
fmt.Println("fib =", o)
}
编译运行输出:
fib = [1 1 2 3 5]
// UpdateSliceE modifies the specified index element of slice
// Note that original slice will not be modified
func UpdateSliceE(slice interface{}, index int, value interface{}) (interface{}, error) {
// check params
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return nil, errors.New("target isn't a slice")
}
if index > v.Len()-1 || reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
return nil, errors.New("param is invalid")
}
dst := reflect.MakeSlice(reflect.TypeOf(slice), 0, 0)
dst = reflect.AppendSlice(dst, v.Slice(0, v.Len()))
dst.Index(index).Set(reflect.ValueOf(value))
return dst.Interface(), nil
}
func main() {
fib = []int{1, 1, 2, 3, 5, 8}
o, _ := UpdateSliceE(fib, 0, 10)
fmt.Println("fib =", fib)
}
编译运行输出:
fib = [10 1 2 3 5 8]
// GetEleIndexesSliceE finds all indexes of the specified element in a slice
func GetEleIndexesSliceE(slice interface{}, value interface{}) ([]int, error) {
// check params
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return nil, errors.New("target isn't a slice")
}
var indexes []int
for i := 0; i < v.Len(); i++ {
if v.Index(i).Interface() == value {
indexes = append(indexes, i)
}
}
return indexes, nil
}
func main() {
fib := []int{}{1, 1, 2, 3, 5, 8}
indexSlice, _ := GetEleIndexesSliceE(fib, 1)
fmt.Println("indexSlice =", indexSlice)
}
编译输出结果:
indexSlice = [0 1]
以上代码已放到开源 Go 工具库 go-huge-util,可直接通过 go mod 方式进行 import 然后使用。
欢迎大家协同共建该工具库。
import (
huge "github.com/dablelv/go-huge-util"
)
fib := []int{1, 1, 2, 3, 5, 8}
r, _ := huge.InsertSliceE(fib, 5, 13) // [1 1 2 3 5 8 13]
r, _ := huge.DeleteSliceE(fib, 0) // [1 2 3 5 8]
r, _ := huge.UpdateSliceE(fib, 5, 88) // [1 1 2 3 5 88]
r, _ := huge.GetEleIndexesSliceE(fib, 1) // [0 1]
// or
r := huge.InsertIntSlice(fib, 5, 13) // [1 1 2 3 5 8 13]
r := huge.DeleteIntSliceE(fib, 0) // [1 2 3 5 8
r := huge.UpdateIntSliceE(fib, 5, 88) // [1 1 2 3 5 88]
r := huge.GetEleIndexesSlice(fib, 1) // [0 1]