【问题标题】:Is there a way to write generic code to find out whether a slice contains specific element in Go?有没有办法编写通用代码来确定切片是否包含 Go 中的特定元素?
【发布时间】:2015-05-03 21:44:12
【问题描述】:

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

我尝试使用interface{} 这样做:

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

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

【问题讨论】:

    标签: go slice contains


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      您可以使用 reflect 来实现,但它会比非泛型等效函数慢得多

      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
      }
      

      我明白了:

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

      【讨论】:

        【解决方案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 循环

        选择什么方式取决于你要解决的问题。

        【讨论】:

          【解决方案4】:

          我不确定您的具体上下文是什么,但您可能希望使用 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

          【讨论】:

            猜你喜欢
            • 2021-12-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-06-13
            • 1970-01-01
            • 2018-06-23
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多