Go 判断数组中是否包含某个 item

Go 中不像 Python 那样可以通过 a in [] 判断数组是否包含某个 item,项目中只能自己编写该方法。

刚入门时第一个想到的方法就是对某个类型的数组进行遍历,再逐个对比,但这样有个最大的问题就是我们只能对一种类型的数组进行对比,不能灵活的应对各种类型。

reflect

在 stackexchange 中看到一个方法,充分的利用了 reflect 包对 interface{} 进行判断,如下

package main

import "reflect"

func Contains(array interface{}, val interface{}) (index int) {
    index = -1
    switch reflect.TypeOf(array).Kind() {
        case reflect.Slice: {
            s := reflect.ValueOf(array)
            for i := 0; i < s.Len(); i++ {
                if reflect.DeepEqual(val, s.Index(i).Interface()) {
                    index = i
                    return
                }
            }
        }
    }
    return
}

一次遍历

2019-03-21 更新:

今天才从网上看到一个说法,Go 中 reflect 很慢,尽量不要使用,我心里咯噔一下,赶紧来测试下

对比下强类型遍历的方法

func StringsContains(array []string, val string) (index int) {
    index = -1
    for i := 0; i < len(array); i++ {
        if array[i] == val {
            index = i
            return
        }
    }
    return
}

使用 testing 包来测试性能

import "testing"

func BenchmarkContains(b *testing.B) {
    sa := []string{"q", "w", "e", "r", "t"}
    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        Contains(sa, "r")
    }
}

func BenchmarkStringsContains(b *testing.B) {
    sa := []string{"q", "w", "e", "r", "t"}
    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        StringsContains(sa, "r")
    }
}

运行

$ go test -bench=.

goos: darwin
goarch: amd64
pkg: github.com/wxnacy/wgo/arrays
BenchmarkContains-8              2000000               635 ns/op
BenchmarkStringsContains-8      100000000               20.3 ns/op
PASS
ok      github.com/wxnacy/wgo/arrays    4.027s

结果非常明显,Contains 方法平均每次 635 nsStringsContains 方法平均每次 20.3 ns

嗯,我得先缓一下,为什么 reflect 这么慢呢,reflect 为什么慢这篇文章给了很好的解释,总结下来有两点

  • 涉及到内存分配以后 GC
  • reflect 实现里面有大量的枚举

不管怎样,reflect 耗时长是不争的事实,又浏览了下其他的文章,大多观点都视它如 “猛虎”,避之不及。

但是静下心来想想,大可不必。慢,也是相对的。毕竟这个例子很简单,为了追求性能,当然可以使用强类型比较,那如果是更复杂的映射操作呢,Go 是强类型语言啊,你不用 reflect 用啥。反射就不能用,你让 SpringMVC 咋办。

wgo

在官方完美解决这个问题前,我们只能在项目中自己写方法,或者使用第三方包 wgo

wgo 是类似 Python 交互命令的脚本化运行工具。

该包本身包含一些开发中常用但是 Go 不具备的方法和工具。

安装

$ go get -u github.com/wxnacy/wgo

导入

import "github.com/wxnacy/wgo/arrays"

常用 Contains 方法

// Contains Returns the index position of the val in array
func Contains(array interface{}, val interface{}) (index int)
// ContainsString Returns the index position of the string val in array
func ContainsString(array []string, val string) (index int)
// ContainsInt Returns the index position of the int64 val in array
func ContainsInt(array []int64, val int64) (index int)
// ContainsFloat Returns the index position of the float64 val in array
func ContainsFloat(array []float64, val float64) (index int)
// ContainsBool Returns the index position of the bool val in array
func ContainsBool(array []bool, val bool) (index int)

demo

package main

import (
    "fmt"
    "github.com/wxnacy/wgo/arrays"
)

func main() {
    var arr = []int64{1, 3, 4, 8, 12, 4, 9}
    var i int
    i = arrays.ContainsInt(arr, 8)
    fmt.Println(i)      // 3

    i = arrays.Contains(arr, int64(12))
    fmt.Println(i)      // 4
}

原文地址:https://wxnacy.com/2018/11/20/go-in-array/