文章目录

  • 1.删除指定类型切片
  • 2.反射范化
  • 3.反射缺点
  • 4.快速使用
  • 参考文献

1.删除指定类型切片

删除切片指定元素,Go 标准库并未给出相应的函数,需要我们自己实现。以 []int 类型的切片为例,我们可能会直接写出下面的函数。

// DeleteSliceElms 删除切片指定元素(不许改原切片)
func DeleteSliceElms(sl []int, elms ...int) []int {if len(sl) == 0 || len(elms) == 0 {return sl}// 先将元素转为 setm := make(map[int]struct{})for _, v := range elms {m[v] = struct{}{}}// 过滤掉指定元素res := make([]int, 0, len(sl))for _, v := range sl {if _, ok := m[v]; !ok {res = append(res, v)}}return res
}// 使用示例
sl := []int{1, 2, 3, 3, 2, 5}
res := DeleteSliceElms(sl, 2, 3) // [1,5]

完全没有问题,上面的函数完美了实现了我们想要的功能。

但是如果我们现在又需要对 []string 类型的切片删除指定的元素,你可能想到的是拷贝一下上面的函数,改下对应的类型即可。

// DeleteStrSliceElms 删除切片指定元素(不许改原切片)
func DeleteStrSliceElms(sl []string, elms ...string) []string {if len(sl) == 0 || len(elms) == 0 {return sl}// 先将元素转为 setm := make(map[string]struct{})for _, v := range elms {m[v] = struct{}{}}// 过滤掉指定元素res := make([]string, 0, len(sl))for _, v := range sl {if _, ok := m[v]; !ok {res = append(res, v)}}return res
}

如此又解决了我们的问题。但是如果我们又需要对其他类型的切片进行删除,难道故技重施,再次拷贝重复的代码吗?

2.反射范化

面对重复的代码,我们应该消灭它,而不是助长它。如何消灭呢,这本该是泛型要做的事情,可惜在 Go(截止 Go 1.17)不支持范型。但是 Go 为我们提供了反射,我们可以利用反射,间接地实现范型的效果:只写一个函数,支持所有类型的切片。

// DeleteSliceElmsE deletes the specified elements from the slice.
// Note that the original slice will not be modified.
func DeleteSliceElmsE(i interface{}, elms ...interface{})(interface{}, error) {// check paramsv := reflect.ValueOf(i)if v.Kind() != reflect.Slice {return nil, errors.New("the input isn't a slice")}if v.Len() == 0 || len(elms) == 0 {return i, nil}if reflect.TypeOf(i).Elem() != reflect.TypeOf(elms[0]) {return nil, errors.New("element type is ill")}// convert the elements to map setm := make(map[interface{}]struct{})for _, v := range elms {m[v] = struct{}{}}// filter out specified elementst := reflect.MakeSlice(reflect.TypeOf(i), 0, v.Len())for i := 0; i < v.Len(); i++ {if _, ok := m[v.Index(i).Interface()]; !ok {t = reflect.Append(t, v.Index(i))}}return t.Interface(), nil
}

如果不关心错误,可以再封装一个函数:

// DeleteSliceElms deletes the specified elements from the slice.
// Note that the original slice will not be modified.
func DeleteSliceElms(i interface{}, elms ...interface{}) interface{} {res, _ := DeleteSliceElmsE(i, elms...)return res
}

使用示例:

sl1 := []int{1, 2, 3, 3, 2, 5}
res1, _ := DeleteSliceElms(sl1, 2, 3).([]int) // [1,5]
sl2 := []string{"foo", "bar", "baz", "bar"}
res2, _ := DeleteSliceElms(sl2, "foo", "bar").([]string) // [baz]

通过反射我们成功消灭了多余重复代码。看似美好,果然如此吗?

3.反射缺点

反射主要用于在运行时检测或修改程序行为,提高程序的灵活性。天下没有免费的午餐,反射带来灵活的同时,也带来了性能问题。

我们通过性能测试对比下通过反射和不通过反射着两种方式的性能差异。

func BenchmarkDeleteStrSliceElmsFast(b *testing.B) {sl := []string{"foo", "bar", "baz", "bar"}for n := 0; n < b.N; n++ {DeleteStrSliceElmsFast(sl, "foo", "bar")}
}func BenchmarkDeleteStrSliceElmsReflect(b *testing.B) {sl := []string{"foo", "bar", "baz", "bar"}for n := 0; n < b.N; n++ {DeleteStrSliceElmsReflect(sl, "foo", "bar")}
}

执行性能测试命令:

go test -bench .
goos: darwin
goarch: amd64
pkg: test/slice
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkDeleteStrSliceElmsFast-12               9195922               123.5 ns/op
BenchmarkDeleteStrSliceElmsReflect-12            2258203               524.0 ns/op
PASS
ok      test/slice      3.338s

可见性能差距接近 5 倍。如果是密集型操作或对性能要求较高的场景,在 Go 支持范型前(听说 Go 1.18 开始支持范型),建议还是乖乖地写对应类型的切片删除函数。

4.快速使用

以上反射版本的实现已经开源至 go-huge-util,欢迎使用。

package mainimport ("fmt"huge "github.com/dablelv/go-huge-util"
)func main() {sl1 := []int{1, 2, 3, 3, 2, 5}res1, _ := huge.DeleteSliceElms(sl1, 2, 3).([]int)sl2 := []string{"foo", "bar", "baz", "bar"}res2, _ := huge.DeleteSliceElms(sl2, "foo", "bar").([]string)fmt.Printf("res1 is %v, res2 is %v\n", res1, res2)
}

运行输出:

res1 is [1 5], res2 is [baz]

关于开源工具库 go-huge-util,欢迎大家协同共建,添砖加瓦。


参考文献

reflect package

Golang 删除切片指定元素相关推荐

  1. python删除列表元素的所有方法_python 列表删除所有指定元素的方法

    python 列表删除所有指定元素的方法 如下所示: a = [1,1,1,2,3,45,1,2,1] a.remove(1) result: [1,1,2,3,45,1,2,1] while 1 i ...

  2. [Js]删除数组指定元素

    写在前面 在最近的项目中,有用到js对数组的操作,之前自己几乎没有用到这种方法,这里就记录一下,算是对学到的东西的一种总结吧. 数组对象splice方法 splice() 方法向/从数组中添加/删除项 ...

  3. JS数组删除指定下标元素 JS删除对象指定元素

    一.JS数组删除指定下标元素 splice方法--Array.splice(index,n) 参数语义化理解:删除以下标index为起点的n个元素.(之后的元素会往前移动) 注意:splice会直接改 ...

  4. jq点击所有子元素_jQuery删除/清空指定元素下的所有子节点的方法

    上几篇文章详细的介绍了,我们可以利有jQuery中的detach()方法和jQuery中的remove()方法删除指定的或是匹配的元素,也可以使用removeClass()方法以及removeAttr ...

  5. php移除所有子节点,jQuery删除/清空指定元素下的所有子节点的方法

    上几篇文章详细的介绍了,我们可以利有jQuery中的detach()方法和jQuery中的remove()方法删除指定的或是匹配的元素,也可以使用removeClass()方法以及removeAttr ...

  6. JavaScript使用for循环和splice删除数组指定元素的注意点

    在JavaScript里可以结合for循环和splice来删除数组指定的元素.但是要注意删除元素后,数组索引会发生改变 示例 var arr = ["a","b" ...

  7. js删除数组指定元素

    前端小记 ES6删除数组中指定元素,例: let array = [1, 2, 3, 4]; 删除2,array.splice(array.findIndex(item => item === ...

  8. es6 删除数组指定元素

    arr.splice(arr.findIndex(item => item.id === id), 1) //item 只是参数可以写成 i 或者 v 都可以 , //后面的额id是数组的id, ...

  9. 微信小程序 循环原数组并追加新元素(删除数组指定元素)

    今天新get到了一个小技能 push和concat的区别: push 遇到数组参数时,把整个数组参数作为一个元素:而 concat 则是拆开数组参数,一个元素一个元素地加进去. push 直接改变当前 ...

最新文章

  1. PostgreSQL 表达式索引 - 语法注意事项
  2. python中的排序方法都有哪些_几种常见的排序方法总结(Python)
  3. Ajax — cropper (图片剪裁)基本用法
  4. Python中字符串String去除出换行符(\n,\r)和空格的问题
  5. oracle查看所有用户6,oracle 查看 用户 用户权限 用户表空间 用户默认表空间
  6. Linux下打开Android调试器DDMS的方法
  7. 软件可靠性的一些关注点
  8. Git操作教程-(全)
  9. Python名词解释
  10. 苏大《实用计算机网络教程》复习题
  11. 联邦学习拜占庭鲁棒差分隐私博弈论
  12. (文献研读)ContainerCloudSim:云数据中心中容器建模和仿真的环境
  13. 酒店:二维码如何应用于酒店管理
  14. D200和D2X区别
  15. Whitelabel Error Page(2)之Internal Server Error
  16. 微信小程序的websocket使用stomp协议--简单实用的npm包
  17. Windows系统安全配置
  18. 复现 Oriented R-CNN RTX3080ti
  19. 【杂项】JZ2440挂载NFS网络文件系统
  20. 十倍效能提升——Web 基础研发体系的建立

热门文章

  1. 谷歌修复多个严重的安卓 RCE 漏洞
  2. 受新冠病毒影响,谷歌延迟发布 Chrome 和 Chrome OS 安全更新版本
  3. VMware 修复 Workstation、Fusion 中多个严重的代码执行漏洞
  4. oracle 移动数据文件(装)
  5. 如何获得select被选中option的value和text和......
  6. JAVA 微信支付 native方式
  7. 大学生魏则西之死,谁该为违法广告买单?
  8. jquery获取元素索引
  9. 转摘:工厂方法模式(Factory Method Pattern)
  10. java中case语句_Java:switch-case语句