我知道我可以遍历映射m

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

寻找一个键,但是有没有更有效的方法来测试一个键在地图中的存在呢?


当前回答

在go-nuts电子邮件列表中搜索,找到了Peter Froehlich在2009年11月15日发布的解决方案。

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语句,value和ok变量只在if条件中可见。

其他回答

在go-nuts电子邮件列表中搜索,找到了Peter Froehlich在2009年11月15日发布的解决方案。

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语句,value和ok变量只在if条件中可见。

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

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

This is nice and clean. It has some restrictions though: it must be an assignment of special form. Right-hand side expression must be the map index expression only, and the left-hand expression list must contain exactly 2 operands, first to which the value type is assignable, and a second to which a bool value is assignable. The first value of the result of this special form will be the value associated with the key, and the second value will tell if there is actually an entry in the map with the given key (if the key exists in the map). The left-hand side expression list may also contain the blank identifier if one of the results is not needed.

重要的是要知道,如果索引映射值为nil或不包含键,则索引表达式计算为映射值类型的零值。例如:

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上试试。

如果我们知道在地图中不使用0值,我们可以利用这个。

例如,如果值类型是字符串,并且我们知道我们从来没有在map中存储值为空字符串的条目(字符串类型为零值),我们也可以通过比较索引表达式(结果)的非特殊形式与零值来测试键是否在映射中:

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

在实践中,有许多情况下,我们不存储零值的值在地图中,所以这可以经常使用。例如,接口和函数类型有一个零值nil,我们通常不存储在映射中。因此,测试一个键是否在映射中可以通过将它与nil进行比较来实现。

使用这种“技术”还有另一个好处:你可以以一种紧凑的方式检查多个键的存在(你不能用特殊的“逗号ok”形式做到这一点)。更多信息:在一个条件下,检查key是否存在于多个映射中

在索引不存在的键时获取值类型的零值还允许我们方便地将bool值作为集合使用。例如:

set := map[string]bool{
    "one": true,
    "two": true,
}

fmt.Println("Contains 'one':", set["one"])

if set["two"] {
    fmt.Println("'two' is in the set")
}
if !set["three"] {
    fmt.Println("'three' is not in the set")
}

它输出(在Go Playground上试试):

Contains 'one': true
'two' is in the set
'three' is not in the set

请参阅相关:如何创建包含唯一字符串的数组?

两个值的赋值可用于此目的。请检查下面的示例程序

package main

import (
    "fmt"
)

func main() {
    //creating a map with 3 key-value pairs
    sampleMap := map[string]int{"key1": 100, "key2": 500, "key3": 999}
    //A two value assignment can be used to check existence of a key.
    value, isKeyPresent := sampleMap["key2"]
    //isKeyPresent will be true if key present in sampleMap
    if isKeyPresent {
        //key exist
        fmt.Println("key present, value =  ", value)
    } else {
        //key does not exist
        fmt.Println("key does not exist")
    }
}

下面是检查映射是否包含键的方法。

val, ok := myMap["foo"]
// If the key exists
if ok {
    // Do something
}

这将初始化两个变量。Val是映射中“foo”的值(如果存在),如果不存在则为“0值”(在本例中为空字符串)。Ok是bool类型,如果键存在,它将被设置为true。

如果愿意,可以将其缩短为一行代码。

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

Go允许您在if语句中的条件(注意分号)之前放置初始化语句。这样做的结果是,ofval和ok的作用域将被限制在if语句的主体中,如果你只需要在那里访问它们,这是很有帮助的。

除了《Go编程语言规范》,你还应该阅读《Effective Go》。在地图的部分,他们说,除了别的以外:

An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing. attended := map[string]bool{ "Ann": true, "Joe": true, ... } if attended[person] { // will be false if person is not in the map fmt.Println(person, "was at the meeting") } Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment. var seconds int var ok bool seconds, ok = timeZone[tz] For obvious reasons this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report: func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0 } To test for presence in the map without worrying about the actual value, you can use the blank identifier (_) in place of the usual variable for the value. _, present := timeZone[tz]