2015-03-03 88 views
3

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

我尝试使用interface{}做到这一点,如:

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

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

回答

4

你可以用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
+0

非常感谢您的基准测试。 – armnotstrong 2015-03-03 10:52:49

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

你可以把它用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循环

什么样的选择取决于你正在解决的问题。

0

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

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

相关问题