这是“编程进入:为21世纪创造应用”一书中的引用:
使用像这样的简单线性搜索是未排序的唯一选项 数据,适用于小切片(最多数百个项目)。但对于 更大的切片,特别是如果我们反复进行搜索的话 线性搜索效率非常低,平均需要一半的项目 每次比较。 Go提供了一个使用二进制搜索的sort.Search()方法 算法:这需要仅比较log2(n)项(其中n 是每次的项目数量。为了正确看待这一点, 线性搜索1000000个项目平均需要500000个比较, 最糟糕的情况是1000000比较;二进制搜索需要 大多数20次比较,即使在最坏的情况下也是如此。
使用像这样的简单线性搜索是未排序的唯一选项 数据,适用于小切片(最多数百个项目)。但对于 更大的切片,特别是如果我们反复进行搜索的话 线性搜索效率非常低,平均需要一半的项目 每次比较。
Go提供了一个使用二进制搜索的sort.Search()方法 算法:这需要仅比较log2(n)项(其中n 是每次的项目数量。为了正确看待这一点, 线性搜索1000000个项目平均需要500000个比较, 最糟糕的情况是1000000比较;二进制搜索需要 大多数20次比较,即使在最坏的情况下也是如此。
files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"} target := "Makefile" sort.Strings(files) i := sort.Search(len(files), func(i int) bool { return files[i] >= target }) if i < len(files) && files[i] == target { fmt.Printf("found \"%s\" at files[%d]\n", files[i], i) }
https://play.golang.org/p/UIndYQ8FeW
在Go中没有内置的操作符。您需要迭代数组。您可以编写自己的函数来执行此操作,如下所示:
func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false }
如果您希望能够在不迭代整个列表的情况下检查成员身份,则需要使用映射而不是数组或切片,如下所示:
visitedURL := map[string]bool { "http://www.google.com": true, "https://paypal.com": true, } if visitedURL[thisSite] { fmt.Println("Already been here.") }
刚刚有类似的问题,并决定尝试在这个线程中的一些建议。
我已经对3种类型的查找的最佳和最差情况进行了基准测试:
这是功能代码:
func belongsToMap(lookup string) bool { list := map[string]bool{ "900898296857": true, "900898302052": true, "900898296492": true, "900898296850": true, "900898296703": true, "900898296633": true, "900898296613": true, "900898296615": true, "900898296620": true, "900898296636": true, } if _, ok := list[lookup]; ok { return true } else { return false } } func belongsToList(lookup string) bool { list := []string{ "900898296857", "900898302052", "900898296492", "900898296850", "900898296703", "900898296633", "900898296613", "900898296615", "900898296620", "900898296636", } for _, val := range list { if val == lookup { return true } } return false } func belongsToSwitch(lookup string) bool { switch lookup { case "900898296857", "900898302052", "900898296492", "900898296850", "900898296703", "900898296633", "900898296613", "900898296615", "900898296620", "900898296636": return true } return false }
最佳案例场景选择列表中的第一项,最坏情况下使用不存在的值。
结果如下:
BenchmarkBelongsToMapWorstCase-4 2000000 787 ns/op BenchmarkBelongsToSwitchWorstCase-4 2000000000 0.35 ns/op BenchmarkBelongsToListWorstCase-4 100000000 14.7 ns/op BenchmarkBelongsToMapBestCase-4 2000000 683 ns/op BenchmarkBelongsToSwitchBestCase-4 100000000 10.6 ns/op BenchmarkBelongsToListBestCase-4 100000000 10.4 ns/op
Switch一路赢,最坏的情况比最好的情况快得多。 地图是最差的,列表更接近切换。
所以道德是: 如果你有一个静态的,相当小的列表,那么switch语句就是你要走的路。
另一种选择是使用地图作为集合。你只使用键,并使值类似于一个总是正确的布尔值。然后,您可以轻松检查地图是否包含密钥。如果您需要一个集合的行为,这很有用,如果您多次添加一个值,它只在集合中一次。
这是一个简单的例子,我将随机数作为键添加到地图中。如果多次生成相同的数字并不重要,它只会出现在最终的地图上一次。然后我使用一个简单的if检查键是否在地图中。
package main import ( "fmt" "math/rand" ) func main() { var MAX int = 10 m := make(map[int]bool) for i := 0; i <= MAX; i++ { m[rand.Intn(MAX)] = true } for i := 0; i <= MAX; i++ { if _, ok := m[i]; ok { fmt.Printf("%v is in map\n", i) } else { fmt.Printf("%v is not in map\n", i) } } }
这里是去游乐场
上面使用sort的例子很接近,但在字符串的情况下只需使用SearchString:
files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"} target := "Makefile" sort.Strings(files) i := sort.SearchStrings(files, target) if i < len(files) && files[i] == target { fmt.Printf("found \"%s\" at files[%d]\n", files[i], i) }
https://golang.org/pkg/sort/#SearchStrings
列表包含静态值的另一种解决方案。
例如:从有效值列表中检查有效值:
func IsValidCategory(category string) bool { switch category { case "auto", "news", "sport", "music": return true } return false }