前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang 任意类型切片的增删改查

Golang 任意类型切片的增删改查

作者头像
恋喵大鲤鱼
发布2022-05-09 11:16:20
8070
发布2022-05-09 11:16:20
举报
文章被收录于专栏:C/C++基础C/C++基础
在这里插入图片描述
在这里插入图片描述

文章目录

slice 名为切片,是 Go 中的可变长数组,是对底层数组的封装和引用。切片指向一个底层数组,并且包含长度和容量信息。未初始化切片的值为 nil。作用于切片的内建函数主要有四个,分别是 make、len、cap 和 append。make 用于创建切片,len 用于获取切片的长度,cap 用于获取切片的容量,append 用于向切片追加元素。

代码语言:javascript
复制
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)
}

输出结果:

代码语言:javascript
复制
len(fib)= 0
cap(fib)= 10
fib= [1 1 2 3 5 8 13]

1.增加元素

1.1 使用 []interface{} 实现

存放任意类型元素的切片可以使用 []interface{} 表示,但不能表示任意切片类型,即具体类型的切片无法转换为 []interface{} ,需要显示转换。

代码语言:javascript
复制
//@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)
}

输出结果:

代码语言:javascript
复制
fib = [1]

注意,[]interface{} 表示存放任意类型元素的切片,并不是任意切片类型,所以使用具体类型的切片时,编译时将出现类型转换错误。比如 []int 切片不能赋值给 []interface{}。

代码语言:javascript
复制
fib := []int{1, 1}
r := insert(fib, 2, 2)

编译错误:

代码语言:javascript
复制
cannot convert fib (type []int) to type []interface {}

如果想对具体类型的切片进行插入,需要显示地将具体类型的切片转换为 []interface{},然后再将结果 []interface{} 切片转换回具体类型的切片。

代码语言:javascript
复制
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)
}

输出结果:

代码语言:javascript
复制
fib = [1 1]

1.2 使用 interface{} 实现

使用 []interface{} 实现,需要的将具体类型的切片转换为 []interface{},再将结果 []interface{} 切片转换为具体类型的切片,使用上非常繁琐。基于 []interface{} 的实现,可以进行进一步的封装。我们使用 interface{} 接收任意切片类型,利用 Go 提供的反射功能,实现对任意切片类型的插入操作。主要有如下步骤: (1)将任意切片类型转换为[]interface{}; (2)对[]interface{}进行插入; (3)将结果[]interface{}切片转换为具体类型的切片。

代码语言:javascript
复制
// 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)
        }
 }

编译运行输出:

代码语言:javascript
复制
fib = [1 1]

1.3 使用 interface{} 的优雅实现

基于 []interface{} 的实现,仍然无法摆脱类型枚举的冗余做法,实际上可以摆脱 []interface{},借助 Golang reflect 包提供的反射功能,完全使用 interface{} 来实现。

代码语言:javascript
复制
// 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)
}

编译运行输出:

代码语言:javascript
复制
fib = [1 1 2 3 5 8]

2.删除元素

代码语言:javascript
复制
// 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)
}

编译运行输出:

代码语言:javascript
复制
fib = [1 1 2 3 5]

3.修改元素

代码语言:javascript
复制
// 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)
}

编译运行输出:

代码语言:javascript
复制
fib = [10 1 2 3 5 8]

4.查找元素下标

代码语言:javascript
复制
// 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)
}

编译输出结果:

代码语言:javascript
复制
indexSlice = [0 1]

5.小结

以上代码已放到开源 Go 工具库 go-huge-util,可直接通过 go mod 方式进行 import 然后使用。

欢迎大家协同共建该工具库。

代码语言:javascript
复制
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]
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1.增加元素
    • 1.1 使用 []interface{} 实现
      • 1.2 使用 interface{} 实现
        • 1.3 使用 interface{} 的优雅实现
        • 2.删除元素
        • 3.修改元素
        • 4.查找元素下标
        • 5.小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档