如何检查地图中是否包含密钥?

我知道我可以遍历一个地图m,

for k, v := range m { ... } 

并寻找一个关键,但有一个更有效的方式来testing一个密钥的存在在地图中? 谢谢。 我无法在语言规范中find答案。

一行答案:

 if val, ok := dict["foo"]; ok { //do something here } 

说明:

if Go中的语句可以同时包含一个条件和一个初始化语句。 上面的例子使用了两个:

  • 初始化两个variables – val会从map中获得“foo”的值或者是一个“零值”(在这个例子中是空string), ok会收到一个bool,如果实际存在“foo”在地图上

  • 评估ok ,如果地图中有“foo”,则为true

如果地图中确实存在“foo”,则if语句的主体将被执行, val将在该范围内。

编辑:以下答案在Go 1之前。从Go 1开始,它不再是准确/有效的。


除了Go编程语言规范之外 ,您还应该阅读Effective Go 。 在地图部分,他们说,除其他外:

“尝试使用地图中不存在的键获取地图值将导致程序崩溃,但是有一种方法可以安全地使用多个任务。”

 var seconds int var ok bool seconds, ok = timeZone[tz] 

“为了testing地图中的存在而不必担心实际值,可以使用空白标识符,简单的下划线(_)。空白标识符可以被赋值或声明为任何types的任何值,并且该值被无害地丢弃。要在地图上testing出现位置,请使用空白标识符代替值的常用variables。

 _, present := timeZone[tz] 

在疯狂的电子邮件列表上search ,find了Peter Froehlich于11/15/2009发布的解决scheme。

 package main import "fmt" func main() { dict := map[string]int {"foo" : 1, "bar" : 2} value, ok := dict["baz"] if ok { fmt.Println("value: ", value) } else { fmt.Println("key not found") } } 

或者更简洁,

 if value, ok := dict["baz"]; ok { fmt.Println("value: ", value) } else { fmt.Println("key not found") } 

注意,使用if语句的这种forms, valueokvariables只在if条件中可见。

简答

 _, exists := timeZone[tz] // Just checks for key existence val, exists := timeZone[tz] // Checks for key existence and retrieves the value 

这是Go游乐场的一个例子 。

更长的答案

根据Effective Go的地图部分:

尝试使用地图中不存在的键获取地图值时,将返回地图中条目types的零值。 例如,如果地图包含整数,查找一个不存在的键将返回0。

有时你需要从零值中区分一个缺失的条目。 有没有“UTC”的条目,或者是空string,因为它根本不在地图中? 您可以使用多种分配forms进行区分。

 var seconds int var ok bool seconds, ok = timeZone[tz] 

出于显而易见的原因,这被称为“逗号确定”成语。 在这个例子中,如果tz存在,秒将被适当地设置并且ok将是真的; 如果没有,秒将被设置为零,ok将是假的。 这里有一个函数,把它和一个不错的错误报告放在一起:

 func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0 } 

要testing映射中的出现而不用担心实际值,可以使用空白标识符(_)代替该值的常用variables。

 _, present := timeZone[tz] 

正如其他答案所指出的,一般的解决scheme是在特殊forms的赋值中使用索引expression式 :

 v, ok = a[x] v, ok := a[x] var v, ok = a[x] var v, ok T = a[x] 

这是很好,干净。 但是它有一些限制:它必须是特殊forms的赋值。 右侧expression式只能是映射索引expression式,而左侧expression式列表必须包含刚好有两个操作数,第一个值types是可赋值的,第二个操作数赋值给bool值。 这个特殊forms的索引expression式的结果的第一个值将是与键关联的值,第二个值将告诉在给定键(如果键存在于该映射中)的情况下地图上是否存在实际的条目。 如果其中一个结果不需要,则左侧expression式列表还可以包含空白标识符 。

重要的是要知道,如果索引映射值是nil或不包含键,则索引expression式将计算为映射值types的零值 。 举个例子:

 m := map[int]string{} s := m[1] // s will be the empty string "" var m2 map[int]float64 // m2 is nil! f := m2[2] // f will be 0.0 fmt.Printf("%q %f", s, f) // Prints: "" 0.000000 

在Go Playground上试试吧。

所以如果我们知道我们不使用地图中的零值,我们可以利用这个优势。

例如,如果值types是string ,并且我们知道我们不会在映射中存储值为空string( stringtypes为零值)的条目,那么我们还可以通过比较非映射值来testing键是否在映射中 – 索引expression式(的结果)的特殊forms为零值:

 m := map[int]string{ 0: "zero", 1: "one", } fmt.Printf("Key 0 exists: %t\nKey 1 exists: %t\nKey 2 exists: %t", m[0] != "", m[1] != "", m[2] != "") 

输出(在Go Playground上试试):

 Key 0 exists: true Key 1 exists: true Key 2 exists: false 

在实践中,有许多情况下,我们不把零值存储在地图中,所以这可以经常使用。 例如,接口和函数types的零值nil ,我们经常不存储在地图中。 所以testing一个关键是否在地图上可以通过比较它来达到nil

使用这个“技术”还有另外一个好处:你可以用一个紧凑的方式检查多个键的存在(你不能用特殊的“逗号”表格来做)。 更多关于这个: 检查一个条件中是否存在多个映射关键字

  var empty struct{} var ok bool var m map[string]struct{} m = make(map[string]struct{}) m["somestring"] = empty _, ok = m["somestring"] fmt.Println("somestring exists?", ok) _, ok = m["not"] fmt.Println("not exists?", ok) 

那么,去运行maps.go somestring存在? 真的不存在? 假

这里更好的方法

 if _, ok := dict["foo"]; ok { //do something here } 

只是使用

 if len(m) == 0 { ... }