Golang 几种判断 Contains/包含 的性能比较

Go 语言判断元素列表里是否包含某个元素,通常有两种方法:遍历列表、转为map后判断是否包含key。

遍历列表

[]string 遍历判断

func ContainsStr(slice []string, element string) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}

	return false
}

[]int 遍历判断

func ContainsInt(slice []int, element int) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}

	return false
}

如果 slice 是其它类型呢?是不是要挨个写判断函数。

go 1.18+ 有个 comparable 类型,定义的可比较类型:

  • Boolean values
  • Integer values
  • Floating point values
  • Complex values
  • String values
  • Pointer values
  • Channel values
  • Interface values
  • Struct values are comparable if all their fields are comparable
  • Array values are comparable if values of the array element type are comparable
  • A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T

就可以写成如下通用函数:

func ContainsGeneric[T comparable](slice []T, element T) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}

	return false
}

使用也很简单

var stringSlice = []string{"item 1", "item 2"}
var intSlice = []int{1, 2, 3}

fmt.Println(Contains(stringSlice, "item 2")) // true
fmt.Println(Contains(intSlice, 4)) // false

转换成 map 判断

把 slice 元素的值作为 map 的 key,如:

sliceLen := 100
mapInt   := map[int]struct{}{}
for i := 0; i < sliceLen; i++ {
	mapInt[i] = struct{}{}
}

判断是否存在,为了与上面函数风格相同就这么写

func ContainsMapStr(mp map[string]struct{}, element string) bool {
	_, ok := mp[element]
	return ok
}

性能比较

测试 30 个元素结果

sliceLen: 30
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkContainsGenericStr
BenchmarkContainsGenericStr-8   	  872523	      1346 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsGenericInt
BenchmarkContainsGenericInt-8   	 5177288	       227.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsStr
BenchmarkContainsStr-8          	  862272	      1353 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsInt
BenchmarkContainsInt-8          	 4781001	       256.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMapStr
BenchmarkContainsMapStr-8       	 1623274	       741.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMapInt
BenchmarkContainsMapInt-8       	 1863303	       626.6 ns/op	       0 B/op	       0 allocs/op

测试 100 个元素结果

sliceLen: 100
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkContainsGenericStr
BenchmarkContainsGenericStr-8   	   66457	     18719 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsGenericInt
BenchmarkContainsGenericInt-8   	  533656	      2182 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsStr
BenchmarkContainsStr-8          	   66378	     17976 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsInt
BenchmarkContainsInt-8          	  560392	      2133 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMapStr
BenchmarkContainsMapStr-8       	  454953	      2694 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMapInt
BenchmarkContainsMapInt-8       	  607315	      1956 ns/op	       0 B/op	       0 allocs/op

性能小结

  • 使用遍历 slice 判断的方式,slice 元素是 comparable 或具体的 stringint,性能差不多;int 类型比 string 类型要快 6 倍左右。
  • 使用 map 查询时,key 的类型 intstring 稍快一点,相差不大。
  • slicemap 性能跟元素个数有关系,可以参见以前的一个测试 Golang slice 和 map 的查询性能比较24

优化方案,首先看元素个数,其次看元素类型(这里指常见的 int/string)。

  • 15 个以下,无论是 int 还是 string,建议用 slice
  • 15 ~ 100 个,尽量转为 int ,如果不能转为 int 就用 map
  • 100 个以上,果断用 map,int 和 string 都差不多
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值