如何从Java中的HashMap中选择一个随机密钥?

use*_*929 15 java random hashmap

我正在使用一个大型的ArrayList<HashMap<A,B>>,我会反复需要从随机的HashMap中选择一个随机密钥(并用它做一些事情).选择随机HashMap是微不足道的,但我该如何从这个HashMap中选择一个随机密钥?

速度很重要(因为我需要这样做10000次并且哈希映射很大),所以只需在[0,9999]中选择一个随机数k,然后.next()在迭代器上执行k次,实际上不是一个选项.类似地,在每个随机选择上将HashMap转换为数组或ArrayList实际上不是一个选项.请在回复之前阅读此内容.

从技术上讲,我认为这应该是可能的,因为HashMap将其键存储在Entry[]内部,并且从数组中随机选择很容易,但我无法弄清楚如何访问它Entry[].因此,任何访问内部的想法Entry[]都非常受欢迎.其他解决方案(只要它们不占用散列图大小的线性时间)也是受欢迎的.

注意:启发式方法很好,所以如果有一种方法可以排除1%的元素(例如,由于多个填充的桶),那就没有问题了.

use*_*383 18

从我的头顶

List<A> keysAsArray = new ArrayList<A>(map.keySet())
Random r = new Random()
Run Code Online (Sandbox Code Playgroud)

然后就是

map.get(keysAsArray.get(r.nextInt(keysAsArray.size()))
Run Code Online (Sandbox Code Playgroud)

  • @user1111929 这取决于您的密钥集是否经常更改。您可以仅在地图中添加或删除某些内容时更新列表。然后获取本身将是恒定时间。 (2认同)

Pet*_*rey 6

您需要访问基础条目表。

// defined staticly
Field table = HashMap.class.getDeclaredField("table");
table.setAccessible(true);
Random rand = new Random();

public Entry randomEntry(HashMap map) {
    Entry[] entries = (Entry[]) table.get(map);
    int start = rand.nextInt(entries.length);
    for(int i=0;i<entries.length;i++) {
       int idx = (start + i) % entries.length;
       Entry entry = entries[idx];
       if (entry != null) return entry;
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

这仍然必须遍历条目以找到存在的条目,因此最坏的情况是O(n),但典型的行为是O(1)。


use*_*929 5

我设法找到一个解决方案,而没有性能损失。我将其张贴在这里,因为它可能会对其他人有所帮助-并可能回答有关此主题的几个未解决的问题(我将在以后搜索)。

您需要的是第二个Set类似于自定义的数据结构来存储密钥-而不是此处建议的列表。类似于列表的数据结构要从中删除项目成本很高。所需的操作是在固定时间内添加/删除元素(以使其与HashMap保持最新),以及选择随机元素的过程。下面的类MySet正是这样做的

class MySet<A> {
     ArrayList<A> contents = new ArrayList();
     HashMap<A,Integer> indices = new HashMap<A,Integer>();
     Random R = new Random();

     //selects random element in constant time
     A randomKey() {
         return contents.get(R.nextInt(contents.size()));
     }

     //adds new element in constant time
     void add(A a) {
         indices.put(a,contents.size());
         contents.add(a);
     }

     //removes element in constant time
     void remove(A a) {
         int index = indices.get(a);
         contents.set(index,contents.get(contents.size()-1));
         contents.remove(contents.size()-1);
         indices.set(contents.get(contents.size()-1),index);
         indices.remove(a);
     }
}
Run Code Online (Sandbox Code Playgroud)