从地图中获取任意键/项

时间:2014-05-05 22:13:05

标签: dictionary go

我是Go的新手,现在我想从地图中获取一个任意项目;这样做的惯用方法是什么?我只能想到这样的事情:

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

我想要的原因是我使用地图来维护一组作业,并且使用地图我可以获得待处理作业或删除O(1)中的已完成作业。我想这应该是一个常见的要求,但是如何在Go中做到这一点并不明显。

7 个答案:

答案 0 :(得分:15)

是否可以讨论从散列表中获取任意密钥是否是常见的要求。其他语言地图实现通常缺少此功能(例如Dictionary in C#

但是,您的解决方案可能是最快的解决方案,但您将使用您无法控制的伪随机算法。虽然目前的实现使用了一个pseduo-random算法,Go Specification并没有给你任何保证它实际上是随机的,只是它不能保证是可预测的:

  

未指定地图上的迭代顺序,并且不保证从一次迭代到下一次迭代都是相同的。

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

答案 1 :(得分:7)

从地图中获取随机密钥只会将第二个计数器作为随机数。

// choices = map[string]...

i := rand.Intn(len(choices))
var k string
for k = range choices {
  if i == 0 {
    break
  }
  i--
}

fmt.Println(k, choices[k])

地图可以包含任何类型的有效密钥,您只需要更改var k string即可。您可以将其捆绑为一个函数:

func randIntMapKey(m map[int]string) int {
    i := rand.Intn(len(m))
    for k := range m {
        if i == 0 {
            return k
        }
        i--
    }
    panic("never")
}

答案 2 :(得分:6)

这是一个通用的版本,尽管效率可能较低:

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

https://play.golang.org/p/0uvpJ0diG4e

答案 3 :(得分:0)

作为“全局”解决方案,因为我是elasticsearch的忠实拥护者,您可以使用其他映射/数组来存储数据,以构建一种倒排字典。

答案 4 :(得分:0)

也许您想要的是一个数组,可以方便地随机访问,尤其是 该容器是随机读取的,但很少更改。

答案 5 :(得分:0)

在本质上不支持 API 的数据结构上强制使用 API 通常不是一个好主意。充其量它会是缓慢的、笨拙的、难以测试、难以调试和不稳定的。 Go 的 map 本身支持 upsertgetdeletelength,但不支持 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

答案 6 :(得分:0)

给你。

并发安全和 O(1)

与地图的一般功能相同,但使用“随机”方法。

示例用法:

package main

import (
    "fmt"
    "sync"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    
    s := NewRandMap()

    s.Add("myKey", "Item1")
    s.Add("myKey2", "Item2")
    s.Add("myKey3", "Item3")

    randomItem := s.Random()
    myItem := randomItem.(string)

    fmt.Println(myItem)
}

数据结构:

type RandMap struct {
    m sync.RWMutex

    // Where the objects you care about are stored.
    container map[string]interface{}

    // A slice of the map keys used in the map above. We put them in a slice
    // so that we can get a random key by choosing a random index.
    keys  []string

    // We store the index of each key, so that when we remove an item, we can
    // quickly remove it from the slice above.
    sliceKeyIndex map[string]int
}

func NewRandMap() *RandMap {
    return &RandMap{
        container: make(map[string]interface{}),
        sliceKeyIndex: make(map[string]int),
    }
}

func (s *RandMap) Add(key string, item interface{}) {
    s.m.Lock()
    defer s.m.Unlock()

    // store object in map
    s.container[key] = item

    // add map key to slice of map keys
    s.keys = append(s.keys, key)

    // store the index of the map key
    index := len(s.keys) - 1
    s.sliceKeyIndex[key] = index
}

func (s *RandMap) Get(key string) interface{} {
    s.m.RLock()
    defer s.m.RUnlock()

    return s.container[key]
}

func (s *RandMap) Remove(key string) {
    s.m.Lock()
    defer s.m.Unlock()

    // get index in key slice for key
    index, exists := s.sliceKeyIndex[key]
    if !exists {
        // item does not exist
        return
    }

    delete(s.sliceKeyIndex, key)

    wasLastIndex := len(s.keys) -1 == index

    // remove key from slice of keys
    s.keys[index] = s.keys[len(s.keys)-1]
    s.keys = s.keys[:len(s.keys)-1]

    // we just swapped the last element to another position.
    // so we need to update it's index (if it was not in last position)
    if !wasLastIndex {
        otherKey := s.keys[index]
        s.sliceKeyIndex[otherKey] = index
    }

    // remove object from map
    delete(s.container, key)
}

func (s *RandMap) Random() interface{} {

    if s.Len() == 0 {
        return nil
    }

    s.m.RLock()
    defer s.m.RUnlock()

    randomIndex := rand.Intn(len(s.keys))
    key := s.keys[randomIndex]

    return s.container[key]
}

func (s *RandMap) PopRandom() interface{} {

    if s.Len() == 0 {
        return nil
    }

    randomIndex := rand.Intn(len(s.keys))
    key := s.keys[randomIndex]

    s.m.RLock()
    item := s.container[key]
    s.m.RUnlock()

    s.Remove(key)

    return item
}

func (s *RandMap) Len() int {
    s.m.RLock()
    defer s.m.RUnlock()

    return len(s.container)
}