使用slice [] reflect.Type作为地图键

时间:2018-04-22 11:27:28

标签: arrays dictionary go hash slice

我有一片reflect.Type个值来定义函数的输入类型,我希望使用sync.Map来缓存结果。

我意识到切片一般不能用作地图键,因此我搜索了一种从多个reflect.Type值创建唯一键的方法。想法包括

  1. 为每个子项使用reflect.Type.String()的串联。 godoc明确表示不要将它用于平等,因此密钥不会是唯一的。
  2. 使用fmt.Sprintf("%#v", subKey)的串联 - 请参阅here。我不知道这些密钥是否是唯一的。
  3. sync.Map创建一个树[深度len(切片)]的地图映射...其中值的查找涉及遍历每个子项的地图树中的更深层。这似乎很慢而且很麻烦,但应该有效。
  4. 对密钥数量进行硬编码,并将其存储为大小为上限的数组。这可行,但当您需要超出限制时,硬编码限制很烦人。
  5. why slices can't be used as keys上的引用表明如何定义切片上的相等性可能不清楚。在这种情况下,当且仅当切片具有相同的长度时,我才希望元素相等。

    找到reflect.Type的字节表示类似于类似问题here的解决方案,真是太棒了。

    另见hashing-reflect-type

1 个答案:

答案 0 :(得分:0)

根据评论中的建议,我使用hashing-reflect-type解决方案实现了一个解决方案,但适用于任何类型的哈希类型。

import (
    "reflect"
    "strconv"
    "strings"
    "sync"
)

// IdHashmap stores the next id as an int64 and a map pointing to the
// unique id for each hashable key
type IdHashmap struct {
    nextId  int64
    hashMap map[interface{}]string
    mutex   sync.Mutex
}

// LoadOrStore returns the existing value for the key if present. 
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
// The value is an a unique id for the object that was passed (or 
// object reference). It is a stringified int64
// representing the number of unique objects passed to this IdHashmap 
// before this object was first passed in
func (hm *IdHashmap) LoadOrStore(subKey interface{}) (hashmapId string) {
    if hm.hashMap == nil {
        hm.hashMap = make(map[interface{}]string)
    }
    // the entry for a given key is only ever written once but read many times
    if id, ok := hm.hashMap[subKey]; ok {
        return id
    }
    hm.mutex.Lock()
    // check for the possibility that someone else wrote a value for 
    // this subKey while this thread waited for the lock
    if id, ok := hm.hashMap[subKey]; ok {
        return id
    }
    id := strconv.FormatInt(hm.nextId, 10)
    hm.nextId++
    hm.hashMap[subKey] = id
    hm.mutex.Unlock()
    return id
}

// LoadOrStoreForSlice gets a unique id for a given slice of objects 
// by concatenating hashmapids of its elements
func (hm *IdHashmap) LoadOrStoreForSlice(keys interface{}) (hashmapId string) {
    if reflect.TypeOf(keys).Kind() == reflect.Slice {
        s := reflect.ValueOf(keys)

        ids := make([]string, s.Len())
        for i := 0; i < s.Len(); i++ {
            ids[i], _ = hm.LoadOrStore(s.Index(i))
        }
        return strings.Join(ids, ",")
    }
    // if not a slice try hashing directly in case the type is hashable
    id, _ := hm.LoadOrStore(keys)
    return id
}