我是Go的新手,现在我想从地图中获取一个任意项目;这样做的惯用方法是什么?我只能想到这样的事情:
func get_some_key(m map[int]int) int {
for k := range m {
return k
}
return 0
}
我想要的原因是我使用地图来维护一组作业,并且使用地图我可以获得待处理作业或删除O(1)中的已完成作业。我想这应该是一个常见的要求,但是如何在Go中做到这一点并不明显。
答案 0 :(得分:15)
是否可以讨论从散列表中获取任意密钥是否是常见的要求。其他语言地图实现通常缺少此功能(例如Dictionary in C#)
但是,您的解决方案可能是最快的解决方案,但您将使用您无法控制的伪随机算法。虽然目前的实现使用了一个pseduo-random算法,Go Specification并没有给你任何保证它实际上是随机的,只是它不能保证是可预测的:
未指定地图上的迭代顺序,并且不保证从一次迭代到下一次迭代都是相同的。
如果您想要更多地控制随机化,您还可以使用您选择的随机化(math/rand
或crypto/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()
答案 3 :(得分:0)
作为“全局”解决方案,因为我是elasticsearch的忠实拥护者,您可以使用其他映射/数组来存储数据,以构建一种倒排字典。
答案 4 :(得分:0)
也许您想要的是一个数组,可以方便地随机访问,尤其是 该容器是随机读取的,但很少更改。
答案 5 :(得分:0)
在本质上不支持 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
答案 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)
}