GolangNote

Golang笔记

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

Permalink

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

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

遍历列表

[]string 遍历判断

Go: range string slice
1
2
3
4
5
6
7
8
9
func ContainsStr(slice []string, element string) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}

	return false
}

[]int 遍历判断

Go: range int slice
1
2
3
4
5
6
7
8
9
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

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

Go: ContainsGeneric
1
2
3
4
5
6
7
8
9
func ContainsGeneric[T comparable](slice []T, element T) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}

	return false
}

使用也很简单

Go: ContainsGeneric 使用
1
2
3
4
5
6
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,如:

Go: slice to map
1
2
3
4
5
sliceLen := 100
mapInt   := map[int]struct{}{}
for i := 0; i < sliceLen; i++ {
	mapInt[i] = struct{}{}
}

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

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

性能比较

测试 30 个元素结果

plaintext: 30 个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 个元素结果

plaintext: 100 个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 的查询性能比较

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

  • 15 个以下,无论是 int 还是 string,建议用 slice
  • 15 ~ 100 个,尽量转为 int ,如果不能转为 int 就用 map
  • 100 个以上,果断用 map,int 和 string 都差不多

本文网址: https://golangnote.com/topic/309.html 转摘请注明来源

Related articles

Golang Range 的性能提升Tip

Go 语言里使用 range 可以方便遍历数组(array)、切片(slice)、字典(map)和信道(chan)。这里主要关注他们的性能。...

Golang 泛型性能初识

编程时,我们通常需要编写“泛型”函数,其中确切的数据类型并不重要。例如,您可能想编写一个简单的函数来对数字进行求和。Go 直到最近才有这个概念,但最近才添加了它(从1.18版开始)。...

Write a Comment to "Golang 几种判断 Contains/包含 的性能比较"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.20 Processed in 1ms