有没有办法编写通用代码来确定切片是否包含Go中的特定元素?

时间:2015-03-03 09:40:13

标签: go slice contains

我想知道是否有一种通用的方法来编写代码来判断一个切片是否包含一个元素,我发现它会经常有用,因为有很多逻辑可以判断特定元素是否已经在切片中然后决定下一步做什么。但似乎没有内置的方法(为了上帝的缘故,为什么?)

我尝试使用interface{}来执行此操作:

func sliceContains(slice []interface{}, elem interface{}) bool {
    for _, item := range slice {
       if item == elem {
          return true
       }
    }
    return false
}

我认为interface{}有点像Java的Object,但显然,我错了。每次遇到一个新的切片结构时,我应该写这个吗?是不是有通用的方法来做到这一点?

4 个答案:

答案 0 :(得分:4)

您可以使用reflect执行此操作,但它将比非通用等效函数 MUCH SLOWER

func Contains(slice, elem interface{}) bool {

    sv := reflect.ValueOf(slice)

    // Check that slice is actually a slice/array. 
    // you might want to return an error here
    if sv.Kind() != reflect.Slice && sv.Kind() != reflect.Array {
        return false
    }

    // iterate the slice
    for i := 0; i < sv.Len(); i++ {

        // compare elem to the current slice element
        if elem == sv.Index(i).Interface() {
            return true
        }
    }

    // nothing found
    return false


}

func main(){
    si := []int {3, 4, 5, 10, 11}
    ss := []string {"hello", "world", "foo", "bar"}

    fmt.Println(Contains(si, 3))
    fmt.Println(Contains(si, 100))
    fmt.Println(Contains(ss, "hello"))
    fmt.Println(Contains(ss, "baz"))

}

慢多少? 约x50-x60较慢: 针对表格的非泛型函数进行基准测试:

func ContainsNonGeneic(slice []int, elem int) bool {
    for _, i := range slice {
        if i == elem {
            return true
        }
    }
    return false
}

我得到了:

  • Generic:N=100000, running time: 73.023214ms 730.23214 ns/op
  • 非通用:N=100000, running time: 1.315262ms 13.15262 ns/op

答案 1 :(得分:3)

你可以使用reflect这样的包来实现它:

func In(s, e interface{}) bool {
    slice, elem := reflect.ValueOf(s), reflect.ValueOf(e)
    for i := 0; i < slice.Len(); i++ {
        if reflect.DeepEqual(slice.Index(i).Interface(), elem.Interface()) {
            return true
        }
    }
    return false
}

游乐场示例:http://play.golang.org/p/TQrmwIk6B4

或者,您可以:

  • 定义一个界面并让你的切片实现它
  • 使用地图代替切片
  • 只写一个简单的for循环

选择何种方式取决于您正在解决的问题。

答案 2 :(得分:0)

我不确定您的具体情况是什么,但您可能希望使用map来检查某些内容是否已存在。

package main

import "fmt"

type PublicClassObjectBuilderFactoryStructure struct {
    Tee string
    Hee string
}

func main() {
    // Empty structs occupy zero bytes.
    mymap := map[interface{}]struct{}{}

    one := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "hey"}
    two := PublicClassObjectBuilderFactoryStructure{Tee: "hola", Hee: "oye"}

    three := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "again"}

    mymap[one] = struct{}{}
    mymap[two] = struct{}{}

    // The underscore is ignoring the value, which is an empty struct.
    if _, exists := mymap[one]; exists {
        fmt.Println("one exists")
    }

    if _, exists := mymap[two]; exists {
        fmt.Println("two exists")
    }

    if _, exists := mymap[three]; exists {
        fmt.Println("three exists")
    }
}

使用地图而不是切片的另一个好处是地图有一个内置的delete函数。 https://play.golang.org/p/dmSyyryyS8

答案 3 :(得分:0)

如果您想要一个完全不同的解决方案,您可以尝试使用Gen等工具提供的代码生成器方法。 Gen为要保留在切片中的每个具体类编写源代码,因此它支持类型安全的切片,使您可以搜索first match of an element

(Gen还提供了一些其他类型的集合,并允许您自己编写。)