【问题标题】:Slice as a key in map切片作为地图中的键
【发布时间】:2013-12-16 08:14:33
【问题描述】:

是否可以使用切片作为键?

这是我的尝试:

h := map[[]string]string{
  []string{"a", "b"} : "ab",
}

编译器给我一个错误invalid map key type []string。所以要么不可能,要么我声明不正确(如果是,那么正确的方法是什么?)。

【问题讨论】:

    标签: go


    【解决方案1】:

    但是,使用数组作为映射键是possible

    package main
    
    import "fmt"
    
    func main() {
        m := make(map[[2]int]bool)
        m[[2]int{1, 2}] = false
        fmt.Printf("%v", m)
    }
    

    【讨论】:

    • 同时 string 可以用作键,例如map[string]bool - 一个字节片不能 map[[]byte]bool。但是 string[]byte 可以互换转换 - 所以这有效:bs := []byte{0xde, 0xad, 0xbe, 0xef} ; mymap[string(bs)] = true
    【解决方案2】:

    不,切片不能用作映射键,因为它们没有定义相等性。

    【讨论】:

      【解决方案3】:

      Volker 已经告诉过这是不可能的,我将通过规范中的示例详细说明为什么会这样。


      Map spec告诉你:

      必须为操作数完全定义比较运算符 == 和 != 密钥类型;因此键类型不能是函数、映射或 切片。

      它已经告诉你切片不能是键,但你也可以在 comparison spec 中检查它:

      切片、映射和函数值不可比较。


      这意味着切片也不能是键,数组可以是键。例如你可以写:

      h := map[[2]string]string{
        [2]string{"a", "b"} : "ab",
      }
      

      【讨论】:

        【解决方案4】:

        根据您的要求和数据的复杂性,您可以使用字符串作为映射键,然后使用切片的哈希作为映射键。

        好消息是您可以将此技术用于任何可以转换为字节切片的内容。

        这是将字符串切片转换为字节切片的快速方法:

        []byte(strings.Join([]string{},""))
        

        这是一个使用 SHA1 的示例:

        type ByteSliceMap struct {
            buf *bytes.Buffer
            m   map[string][]byte
        }
        
        func (b *ByteSliceMap) key(buf []byte) string {
            h := sha1.New()
            h.Write(buf)
            sum := h.Sum(nil)
            return fmt.Sprintf("%x", sum)
        }
        
        func (t *ByteSliceMap) value(key []byte) (value []byte, ok bool) {
            value, ok = t.m[t.key(key)]
            return
        }
        
        
        func (t *ByteSliceMap) add(key, value []byte) {
            if t.m == nil {
                t.m = make(map[string][]byte)
            }
            t.m[t.key(key)] = value
        }
        

        Working version

        【讨论】:

        • 这是一个糟糕的选择。即使检查是否存在键也需要加载 GC,因为必须创建多个对象才能进行查找。
        • @DrewO'Meara 你有什么建议或替代方案吗?
        • 这个答案结合了两种技术:使用散列从长字节片构建短字节片键将字节片转换为字符串。将字节切片转换为字符串string(myByteSlice) 就足够了。转换执行动态分配(-> GC)。散列部分可以重构为不执行分配(通过使用数组和使用例如 Segment 的 fasthash)。
        【解决方案5】:

        解决此问题的一种方法是从具有明确定义的比较运算符的切片中实际创建一个键:

        func createKey(s []string) string { return fmt.Sprintf("%q", s) }
        
        m := make(map[string]string)
        s := []string{"a","b"}
        m[createKey(s)] = "myValue"
        

        以类似的方式,您必须创建函数来创建类型不同于字符串的切片键。

        【讨论】:

        • 这是一个糟糕的选择。即使检查键是否存在也需要加载 GC,因为必须创建一个新对象才能进行查找。不酷。
        • 使用 int 哈希会绕过 GC 头痛吗?
        猜你喜欢
        • 2021-09-21
        • 2019-05-04
        • 1970-01-01
        • 1970-01-01
        • 2017-05-13
        • 2016-11-29
        • 2021-05-02
        • 2020-12-19
        • 2016-05-23
        相关资源
        最近更新 更多