Is there a way to write generic code to find out w

2019-02-25 01:15发布

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?

4条回答
在下西门庆
2楼-- · 2019-02-25 01:25

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

查看更多
小情绪 Triste *
3楼-- · 2019-02-25 01:30

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.

查看更多
甜甜的少女心
4楼-- · 2019-02-25 01:32

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.)

查看更多
看我几分像从前
5楼-- · 2019-02-25 01:40

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
查看更多
登录 后发表回答