我刚学习Go语言,现在想从map中获取任意一个元素;有没有更符合惯用方式的方法?我只能想到类似下面这样的代码:
func get_some_key(m map[int]int) int {
for k := range m {
return k
}
return 0
}
我想要这样做的原因是,我正在使用一个映射来维护一组作业,并且使用映射,我可以在O(1)时间内获取未完成的作业或删除已完成的作业。我猜这应该是一个常见的需求,但在Go中如何实现并不明显。
我刚学习Go语言,现在想从map中获取任意一个元素;有没有更符合惯用方式的方法?我只能想到类似下面这样的代码:
func get_some_key(m map[int]int) int {
for k := range m {
return k
}
return 0
}
我想要这样做的原因是,我正在使用一个映射来维护一组作业,并且使用映射,我可以在O(1)时间内获取未完成的作业或删除已完成的作业。我猜这应该是一个常见的需求,但在Go中如何实现并不明显。
var k, v int; for k, v = range m { break }
如果你想要内联执行的话。为了确保你得到一个值,你可以这样做:var k, v int; var ok bool; for k, v = range m { ok = true; break }
- ANisusslice[rand.Intn(len(slice))]
- ANisusdict.First()
。它并不是在Dictionary中实现的,而是在Linq中实现的,但它确切地做到了这里所要求的。 - TheJP这里有一个更通用的版本,虽然可能不够高效:
keys := reflect.ValueOf(mapI).MapKeys()
return keys[rand.Intn(len(keys))].Interface()
请看这里。
并发安全且O(1)
这是一个为map添加“随机”方法的包装器。
使用示例:
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
}
s.m.RLock()
randomIndex := rand.Intn(len(s.keys))
key := s.keys[randomIndex]
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)
}
我发现了一种更快的方法来做这件事:
在我的测试中,我创建了以下函数
type ItemType interface{}
func getMapItemRandKey(m map[string]ItemType) string {
return reflect.ValueOf(m).MapKeys()[0].String()
}
b := new(big.Int)
rbytes := (some random function to generate cryptographically safe random bytes)
b.SetBytes(rbytes)
key := b.String()
m := map[string]ItemType
m[key] = &ItemType{}
var key string
var val string
for k, v := range myMap {
key = k
val = v
break
}
对于多个键,你可以这样做:
func split_map(myMap map[string]string, idx int) (string[], string[]) {
keys := make([]string, len(myMap))
values := make([]string, len(myMap))
count := 0
for k, v := range myMap {
keys[count] = k
values[count] = v
count = count + 1
}
return keys, values
}
当访问第i个元素时,
func get_ith(myMap map[string]string, idx int) (string, string) {
count := 0
for k, v := range myMap {
if idx == count {
return k, v
}
count = count + 1
}
return "", ""
}
通常强制将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]
。 - Dmitri Goldringsync.Mutex
保护检索/删除操作,以便两个goroutine不会获取相同的作业(因为为了速度而不是本地线程安全)。 - twotwotwo