【问题标题】:Nested maps in GolangGolang 中的嵌套地图
【发布时间】:2017-11-02 11:44:15
【问题描述】:
func main() {
    var data = map[string]string{}
    data["a"] = "x"
    data["b"] = "x"
    data["c"] = "x"
    fmt.Println(data)
}

它运行。

func main() {
    var data = map[string][]string{}
    data["a"] = append(data["a"], "x")
    data["b"] = append(data["b"], "x")
    data["c"] = append(data["c"], "x")
    fmt.Println(data)
}

它也会运行。

func main() {
    var w = map[string]string{}
    var data = map[string]map[string]string{}
    w["w"] = "x"
    data["a"] = w
    data["b"] = w
    data["c"] = w
    fmt.Println(data)
}

它又运行了!

func main() {
    var data = map[string]map[string]string{}
    data["a"]["w"] = "x"
    data["b"]["w"] = "x"
    data["c"]["w"] = "x"
    fmt.Println(data)
}

但它失败了!?

Go 中的嵌套地图有问题吗?还是嵌套地图不支持多括号?

【问题讨论】:

    标签: dictionary go square-bracket


    【解决方案1】:

    地图类型的zero valuenil。它尚未初始化。您不能将值存储在 nil 映射中,这是运行时恐慌。

    在上一个示例中,您初始化了(外部)data 映射,但它没有条目。当您像data["a"] 一样对其进行索引时,由于其中还没有带有"a" 键的条目,因此索引它会返回值类型的零值,即映射的nil。所以尝试分配给data["a"]["w"] 是运行时恐慌。

    你必须先初始化一个地图,然后才能在其中存储元素,例如:

    var data = map[string]map[string]string{}
    
    data["a"] = map[string]string{}
    data["b"] = make(map[string]string)
    data["c"] = make(map[string]string)
    
    data["a"]["w"] = "x"
    data["b"]["w"] = "x"
    data["c"]["w"] = "x"
    fmt.Println(data)
    

    输出(在Go Playground上试试):

    map[a:map[w:x] b:map[w:x] c:map[w:x]]
    

    请注意,当您声明一个映射类型的变量并使用composite literal(如var data = map[string]string{})对其进行初始化时,这也算作初始化。

    请注意,您也可以使用复合文字初始化嵌套映射:

    var data = map[string]map[string]string{
        "a": map[string]string{},
        "b": map[string]string{},
        "c": map[string]string{},
    }
    
    data["a"]["w"] = "x"
    data["b"]["w"] = "x"
    data["c"]["w"] = "x"
    fmt.Println(data)
    

    输出是一样的。在Go Playground 上试试吧。

    【讨论】:

    • 我意识到我正在用 php 习惯编写 golang :) 很难习惯于完全类型安全的编码。但我喜欢它,非常感谢你的回答。
    • 在迭代填充地图的情况下,您可以在分配之前检查 nil 值:if data["c"] == nil { data["c"] = map[string]string{}} 这有助于避免丢失地图中的数据。
    【解决方案2】:

    虽然此问题最直接的答案是如前所述初始化嵌套地图,但根据您的访问模式,还有另一个可能的选择。如果您需要一个真正的分层地图系统,那么之前的答案就可以了。但是,如果您只需要使用多个方面在地图中查找值,请继续阅读!

    map 使用 structs 作为键是完全可以接受的(事实上,任何 comparable 都可以使用)。因此,您可以使用带有结构键的单个映射,例如 this example from the Golang blog,这是一个按国家/地区跟踪页面点击的点击计数器:

    type Key struct {
      Path, Country string
    }
    
    hits := make(map[Key]int)
    
    // set: Vietnamese person visiting the home page
    hits[Key{"/", "vn"}]++
    
    // get: see how many Chinese persons read the spec
    n := hits[Key{"/ref/spec", "cn"}]
    

    我没有经常看到这样的地图,相反,很多人会首先使用嵌套变体,我认为这可能并不总是合适的。

    【讨论】:

    • 优秀的答案!虽然它没有以状态的形式回答问题,但它实际上提供了一种更加健壮的解决方案,而且不会对未初始化的条目造成恐慌。
    【解决方案3】:

    除了icza的回答。地图初始化可以写在short form

    var data = map[string]map[string]string{
        "a": map[string]string{
            "w": "x"},
        "b": map[string]string{
            "w": "x"},
        "c": map[string]string{
            "w": "x"},
        "d": map[string]string{},
    }
    fmt.Println(data)
    

    输出是一样的。在Go Playground 上试试。添加键“d”以演示与空映射的映射。

    【讨论】:

      【解决方案4】:

      以下解决方案可能对您有用。

      var data = map[string]interface{}{
              "publishInfo": map[string]interface{}{
                  "title":       publishInfo.Title,
                  "description": publishInfo.Desc,
                  "thumbnail":   publishInfo.ImageSrc,
                  "url":         publishInfo.URL,
                  "tags":        publishInfo.Tags,
              },
              "revision": draftInfo.Revision,
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-06
        • 2018-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多