Golang 中的嵌套地图

Posted

技术标签:

【中文标题】Golang 中的嵌套地图【英文标题】:Nested maps in Golang 【发布时间】: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 中的嵌套地图有问题吗?还是嵌套地图不支持多括号?

【问题讨论】:

【参考方案1】:

地图类型的zero value 是nil。它尚未初始化。您不能将值存储在 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,
    

【讨论】:

以上是关于Golang 中的嵌套地图的主要内容,如果未能解决你的问题,请参考以下文章

golang golang flatten嵌套地图

golang golang解析嵌套的地图

golang中的动态嵌套结构

golang Go中的地图声明

golang 将func放入go中的地图中

golang 允许你在go中嵌入一个结构中的地图