2015-03-03 84 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還提供了一些其他種類的收集,並允許您自己寫。)

相關問題