Dictionary 从映射中获取任意键/项

Dictionary 从映射中获取任意键/项,dictionary,go,Dictionary,Go,我是新手,现在我想从地图上获取任意项目;惯用的方法是什么?我只能这样想: func get_some_key(m map[int]int) int { for k := range m { return k } return 0 } 我希望这样做的原因是我正在使用一个映射来维护一组作业,并且通过一个映射,我可以在O(1)中获得一个挂起的作业或删除一个已完成的作业。我想这应该是一个常见的要求,但在围棋中如何做到这一点并不明显 可以讨论从哈希表获取任意密钥是

我是新手,现在我想从地图上获取任意项目;惯用的方法是什么?我只能这样想:

func get_some_key(m map[int]int) int {
    for k := range m {
        return k
    }
    return 0
}

我希望这样做的原因是我正在使用一个映射来维护一组作业,并且通过一个映射,我可以在O(1)中获得一个挂起的作业或删除一个已完成的作业。我想这应该是一个常见的要求,但在围棋中如何做到这一点并不明显

可以讨论从哈希表获取任意密钥是否是一个常见要求。其他语言映射实现通常缺少此功能(例如)

然而,您的解决方案可能是最快的,但是您将得到一个您无法控制的伪随机算法。虽然当前的实现使用伪随机算法,但并不能保证它实际上是随机的,只是不能保证它是可预测的:

映射上的迭代顺序没有指定,也不能保证从一次迭代到下一次迭代的顺序相同


如果您希望对随机化进行更多控制,您还可以使用您选择的随机化(
math/rand
crypto/rand
用于更极端的情况)并行保留地图中包含的值(或键)的更新片段,以获取存储在索引中的值,随机选择,在切片中。

这里有一个更通用的版本,尽管它可能效率较低:

    keys := reflect.ValueOf(mapI).MapKeys()
    return keys[rand.Intn(len(keys))].Interface()

作为一个“全局”解决方案,由于我是elasticsearch的忠实粉丝,您可以使用另一个地图/数组来存储数据,构建一种倒排字典。

也许您想要的是一个数组,它很容易随机访问,尤其是
容器是随机读取的,但很少更改。

在我的例子中,地图只有一个键,我需要提取该键,以便您可以执行以下操作:

var键字符串
变量字符串
对于k,v:=范围myMap{
键=k
val=v
打破
}
对于多个键,您可以执行以下操作:

func分割映射(myMap映射[string]string,idx int)(string[],string[]){
键:=make([]字符串,len(myMap))
值:=make([]字符串,len(myMap))
计数:=0
对于k,v:=范围myMap{
键[计数]=k
值[计数]=v
计数=计数+1
}
返回键、值
}
在访问第i个元素时

func get_ith(myMap map[string]string,idx int)(string,string){
计数:=0
对于k,v:=范围myMap{
如果idx==计数{
返回k,v
}
计数=计数+1
}
返回“”“”
}

在本质上不支持API的数据结构上强制API通常不是一个好主意。充其量,它将是缓慢的,黑客,难以测试,难以调试和不稳定的。Go的
map
本机支持
upsert
get
delete
length
,但不支持
GetRandom

在这里提到的两个具体解决方案中

  • 在一个范围内迭代并选择第一个并不保证将选择一个随机项,也不保证对随机度(即均匀、高斯和种子)进行任何控制
  • 反射速度慢,并且需要与贴图大小成比例的额外内存
其他解决方案讨论使用其他数据结构来帮助地图支持此操作。这是我认为最有意义的

type RandomizedSet interface {
    Delete(key int) // O(1)
    Get(key int) int // O(1)
    GetRandomKey() int // O(1)
    Len() int // O(1)
    Upsert(key int, val int) // O(1)
}

type randomizedset struct {
    h map[int]int // map key to its index in the slice
    indexes []int // each index in the slice contains the value
    source rand.Source // rng for testability, seeding, and distribution
}

func New(source rand.Source) RandomizedSet {
    return &randomizedset{
        h: make(map[int]int, 0),
        indexes: make([]int, 0),
        source: source,
    }
}

// helper to accomodate Delete operation
func (r *randomizedset) swap(i, j int) {
    r.indexes[i], r.indexes[j] = r.indexes[j], r.indexes[i]
    r.h[r.indexes[i]] = i
    r.h[r.indexes[j]] = j
}

// remainder of implementations here


您是否知道要获取或设置的密钥的值,或者您是否正在尝试查找随机密钥或事先不知道的某个密钥?您是否正在查找具有特定值的密钥?如果您只是在寻找与键相关联的值,那么它只是
m[i]
。您的方法看起来不错。如果两个goroutine可以同时访问映射,请使用
sync.Mutex
来保护检索/删除操作,这样两个goroutine就不会占用同一个作业(而且因为为了速度起见,映射本身不是线程安全的)。@dehtron5000我不需要随机键或其他东西,我只需要给我一个地图上任何元素的键,如果有的话any@twotwotwo是的,我从文章中看到了这一点,为了清楚起见,我使用的是sync.RWMutex。谢谢你的回答,请看我上面的评论,我不需要任何随机化控制,我只想要一种“从地图中给我一个元素”的方式如果有。@chuchao333,那么您的解决方案是好的。另一种类似的方法是
var k,v int;对于k,v=range m{break}
,如果您希望以内联方式执行此操作。为了确保得到一个值,您可以这样做:
var k,v int;var-ok-bool;对于k,v=range m{ok=true;break}
你最后一句话中的“从切片中获取索引”是什么意思?@jochen:可能用词不当。我的意思是获取存储在切片中特定索引处的值。例如:
slice[rand.Intn(len(slice))]
@ANisus:但问题是关于地图,而不是切片?我看不到一种简单的方法来获取所有键或类似键的切片。它的运行速度比Xeoncross建议的方法慢5倍左右。(虽然我喜欢这个版本,因为它更简单。)我在这里做了一个测试代码:这更糟糕!这是O(N)运行时和内存!