dongza3124
2015-03-03 09:40
浏览 285
已采纳

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

I want to know is there a generic way to write code to judge whether a slice contains an element, I find it will frequently useful since there is a lot of logic to fist judge whether specific elem is already in a slice and then decide what to do next. But there seemed not a built-in method for that(For God's sake, why?)

I try to use interface{} to do that like:

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

I thought interface{} is sort of like Object of Java, but apparently, I was wrong. Should I write this every time meet with a new struct of slice? Isn't there a generic way to do this?

图片转代码服务由CSDN问答提供 功能建议

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

我尝试使用 interface {} 来做到这一点:< / p>

  func sliceContains(slice [] interface {},elem interface {})bool {
表示_,item:=范围切片{
如果item == elem {\  n返回true 
} 
} 
返回false 
} 
   
 
 

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

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

4条回答 默认 最新

  • duanlu1922 2015-03-03 10:18
    已采纳

    You can do it with reflect, but it will be MUCH SLOWER than a non-generic equivalent function:

    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"))
    
    }
    

    How much slower? about x50-x60 slower: Benchmarking against a non generic function of the form:

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

    I'm getting:

    • Generic: N=100000, running time: 73.023214ms 730.23214 ns/op
    • Non Generic: N=100000, running time: 1.315262ms 13.15262 ns/op
    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • duanchui1251 2015-03-03 10:13

    I'm not sure what your specific context is, but you'll probably want to use a map to check if something already exists.

    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")
        }
    }
    

    Another advantage of using maps instead of a slice is that there is a built-in delete function for maps. https://play.golang.org/p/dmSyyryyS8

    评论
    解决 无用
    打赏 举报
  • dou2347 2015-03-03 10:16

    You can make it using the reflect package like that:

    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
    }
    

    Playground examples: http://play.golang.org/p/TQrmwIk6B4

    Alternatively, you can:

    • define an interface and make your slices implement it
    • use maps instead of slices
    • just write a simple for loop

    What way to choose depends on the problem you are solving.

    评论
    解决 无用
    打赏 举报
  • doukang7858 2015-03-04 13:44

    If you want a rather different solution, you might try the code-generator approach offered by tools such as Gen. Gen writes source code for each concrete class you want to hold in a slice, so it supports type-safe slices that let you search for the first match of an element.

    (Gen also offers a few other kinds of collection and allows you to write your own.)

    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题