【问题标题】:Initializing a Go map in a single statement在单个语句中初始化 Go 地图
【发布时间】:2016-12-10 17:29:09
【问题描述】:

这是我的代码:

var keys map[int]string
keys = make(map[int]string)

keys[1] = "aa"
keys[2] = "ab"
keys[3] = "ac"
keys[4] = "ba"
keys[5] = "bb"
keys[6] = "bc"
keys[7] = "ca"
keys[8] = "cb"
keys[9] = "cc"

我可以在一个语句和/或一行中做同样的事情吗?

【问题讨论】:

    标签: dictionary go initialization


    【解决方案1】:

    是的,您可以使用单个语句创建地图(在规范中称为 composite literal):

    var keys = map[int]string{
        1: "aa",
        2: "ab",
        3: "ac",
        4: "ba",
        5: "bb",
        6: "bc",
        7: "ca",
        8: "cb",
        9: "cc",
    }
    

    或者,如果你在函数内部,你可以使用short variable declaration

    keys := map[int]string{
        1: "aa",
        2: "ab",
        3: "ac",
        4: "ba",
        5: "bb",
        6: "bc",
        7: "ca",
        8: "cb",
        9: "cc",
    }
    

    【讨论】:

      【解决方案2】:

      当键和值之间存在逻辑时,您还可以使用循环来初始化映射。将逻辑“放入”循环体中。这可能比使用composite literal 枚举所有键值对要短得多,尤其是在键值对的数量很大的情况下。

      你的例子可以用这个来实现:

      m := map[int]string{}
      for i := 0; i < 9; i++ {
          m[i+1] = string("abc"[i/3]) + string("abc"[i%3])
      }
      fmt.Println(m)
      

      输出(在Go Playground上试试):

      map[5:bb 8:cb 4:ba 2:ab 3:ac 6:bc 7:ca 9:cc 1:aa]
      

      此解决方案的变体(使用不同的逻辑实现):

      m := map[int]string{}
      for i := 0; i < 9; i++ {
          m[i+1] = "abc"[i/3:i/3+1] + "abc"[i%3:i%3+1]
      }
      fmt.Println(m)
      

      输出是“相同的”。在Go Playground 上试试这个变体。

      还有更多解决方案,现在只发布循环体(游乐场链接:another #1another #2):

      // Another #1:
      m[i+1] = fmt.Sprintf("%c%c", "abc"[i/3], "abc"[i%3])
      // Another #2:
      m[i+1] = fmt.Sprintf("%c%c", 'a'+i/3, 'a'+i%3)
      

      另一种方法可能会使用 2 个循环(嵌入式)来生成值,并根据值计算键:

      for i := 'a'; i <= 'c'; i++ {
          for j := 'a'; j <= 'c'; j++ {
              m[int((i-'a')*3+j-'a'+1)] = string(i) + string(j)
          }
      }
      

      Go Playground 上试试这个。

      如果值的数量不大,另一种可行的方法是枚举一个 string 值中的所有元素,并使用子切片(这是有效的,因为不会创建新的后备数组,字符串是共享的):

      const s = "aaabacbabbbccacbcc"
      
      m := map[int]string{}
      for i := 0; i < 9; i++ {
          m[i+1] = s[i*2 : i*2+2]
      }
      fmt.Println(m)
      

      输出(在Go Playground 上试试这个):

      map[9:cc 1:aa 2:ab 5:bb 8:cb 3:ac 4:ba 6:bc 7:ca]
      

      另请注意,如果键的类型为 int 并且键集(或多或少)是连续的,则通常使用切片更有效(无论是内存还是性能):

      m := make([]string, 10)
      for i := 0; i < 9; i++ {
          m[i+1] = fmt.Sprintf("%c%c", 'a'+i/3, 'a'+i%3)
      }
      fmt.Printf("%q\n", m)
      
      m2 := []string{"", "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"}
      fmt.Printf("%q\n", m2)
      
      m3 := []string{1: "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"}
      fmt.Printf("%q\n", m3)
      

      输出(在Go Playground 上试试):

      ["" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc"]
      ["" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc"]
      ["" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc"]
      

      正如您在第三个示例m3 中看到的那样,您可以在复合文字中使用可选索引来指定后面值的索引。更多信息在这里:Keyed items in golang array initialization

      【讨论】:

        【解决方案3】:

        我首选的方法是在短变量声明中使用复合文字。在某些情况下,一个函数可能有助于减少混乱。

        package main
        
        import (
            "fmt"
        )
        
        // initMap initializes a map with an integer key starting at 1
        func initMap(sa []string) map[int]string {
            m := make(map[int]string, len(sa))
            for k, v := range sa {
                m[k+1] = v // add 1 to k as it is starting at base 0
            }
            return m
        }
        
        // main is the entry point of any go application
        func main() {
            // My preferred approach is a composite literal in a short variable declaration
            keys := map[int]string{1: "aa", 2: "ab", 3: "ac", 4: "ba", 5: "bb", 6: "bc", 7: "ca", 8: "cb", 9: "cc"}
            fmt.Println(keys)
        
            // Using a function to initialize the map might help to avoid clutter
            keys2 := initMap([]string{"aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"})
            fmt.Println(keys2)
        }
        

        https://play.golang.org/p/Rrb9ChBkXW查看它的实际应用

        【讨论】:

        • 你的initMap 函数应该为地图提供一个初始容量作为make 的第二个参数:len(sa)
        • 容量对于切片很重要,但地图的工作方式不同并且没有容量。你是对的,如果是大地图,你可以而且应该在 make 语句中提供一个可选的 size 参数。对于小地图,例如本例,您可能无法识别出任何可衡量的性能提升。
        • 很好,按照规范将其称为“初始空间”。关键是它避免了后续分配和重组地图的需要。鉴于其当前分配,这是“避免表增长的能力”。
        猜你喜欢
        • 2019-06-05
        • 1970-01-01
        • 2010-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-12
        • 1970-01-01
        • 2013-10-16
        相关资源
        最近更新 更多