47

有没有办法在 Java 中随机获取 HashMap 的值?

4

14 回答 14

59

这有效:

Random generator = new Random();
Object[] values = myHashMap.values().toArray();
Object randomValue = values[generator.nextInt(values.length)];

如果您希望随机值是一种类型,而不是Object简单地将强制转换添加到最后一行。所以如果myHashMap被声明为:

Map<Integer,String> myHashMap = new HashMap<Integer,String>();

最后一行可以是:

String randomValue = (String) values[generator.nextInt(value.length)];

下面的不起作用Set.toArray()总是返回一个Objects 的数组,它不能被强制转换成一个 s 的数组Map.Entry

Random generator = new Random();
Map.Entry[] entries = myHashMap.entrySet().toArray();
randomValue = entries[generator.nextInt(entries.length)].getValue();
于 2009-05-30T11:23:10.903 回答
33

由于要求仅要求从 中获取随机值,HashMap因此方法如下:

  1. HashMap有一个values方法,它返回地图中的一个值Collection
  2. Collection用于List创建.
  3. size方法用于查找 的大小,ListRandom.nextInt方法用于获取 的随机索引List
  4. 最后,List get使用随机索引从方法中检索该值。

执行:

HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Hello", 10);
map.put("Answer", 42);

List<Integer> valuesList = new ArrayList<Integer>(map.values());
int randomIndex = new Random().nextInt(valuesList.size());
Integer randomValue = valuesList.get(randomIndex);

这种方法的好处是所有方法都是通用的——不需要类型转换。

于 2009-05-30T11:52:42.280 回答
11

如果您需要在不重复任何元素的情况下从地图中绘制更多值,您可以将地图放入 List 中,然后对其进行洗牌。

List<Object> valuesList = new ArrayList<Object>(map.values());
Collections.shuffle( valuesList );

for ( Object obj : valuesList ) {
    System.out.println( obj );
}
于 2009-05-30T12:29:02.813 回答
3

生成一个介于 0 和HashMap. 在随机数处获取密钥。从该键中获取值。

伪代码

 int n =  random(map.keys().length());
 String key = map.keys().at(n);
 Object value = map.at(key);

如果很难在 Java 中实现这一点,那么您可以toArray()使用Set.

 Object[] values = map.values().toArray(new Object[map.size()]);
 Object random_value = values[random(values.length)];

我不太确定如何做随机数。

于 2009-05-30T11:11:24.250 回答
2

当它在热路径中时,将其转换为数组然后获取值太慢了。

所以获取集合(键或键值集)并执行以下操作:

    public class SetUtility {
        public static<Type> Type getRandomElementFromSet(final Set<Type> set, Random random) {
        final int index = random.nextInt(set.size());

        Iterator<Type> iterator = set.iterator();

        for( int i = 0; i < index-1; i++ ) {
            iterator.next();
        }

        return iterator.next();
    }
于 2015-05-29T19:30:07.340 回答
1

一个好的答案稍微取决于具体情况,特别是您需要多长时间为给定地图获取随机密钥(注意,无论您获取密钥还是值,该技术本质上都是相同的)。

  • 如果您需要 来自给定映射的各种随机键,并且在获取随机键之间没有更改映射,则在遍历键集时使用随机采样方法。实际上,您所做的是遍历 keySet() 返回的集合,并在每个项目上计算想要获取该密钥的概率,考虑到您总共需要多少以及到目前为止您已经获取的数量。然后生成一个随机数,看看这个数字是否低于概率。(注意,即使您只需要 1 个密钥,此方法也将始终有效;在这种情况下,它不一定是最有效的方法。)
  • HashMap 中的键实际上已经是伪随机顺序。在极端情况下,对于给定的可能映射,您只需要一个随机键,您甚至可以只提取keySet() 的第一个元素
  • 其他情况下(对于给定的可能映射,您需要多个可能的随机键,或者在您使用随机键之间映射会发生变化),您基本上必须 创建或维护一个键的数组/列表,从中选择一个随机键钥匙。
于 2009-05-30T13:07:33.783 回答
1

通常你并不真正想要一个随机值,而只是想要任何值,然后这样做很好:

Object selectedObj = null;
for (Object obj : map.values()) {
    selectedObj = obj;
    break;
}
于 2012-05-07T12:07:20.137 回答
1

如果您使用的是 Java 8,请使用findAny一个漂亮的解决方案:

MyEntityClass myRandomlyPickedObject = myHashMap.values().stream().findAny();
于 2020-11-12T18:52:06.577 回答
0

下面是一个示例,如何使用 Peter Stuifzand 描述的数组方法,也可以通过values()- 方法:

// Populate the map
// ...

Object[] keys = map.keySet().toArray();
Object[] values = map.values().toArray();

Random rand = new Random();

// Get random key (and value, as an example)
String randKey = keys[ rand.nextInt(keys.length) ];
String randValue = values[ rand.nextInt(values.length) ];

// Use the random key
System.out.println( map.get(randKey) );
于 2009-05-30T12:04:16.220 回答
0

我真的不知道你为什么要这样做......但如果它有帮助,我已经创建了一个 RandomMap,它会在你调用 values() 时自动随机化这些值,那么下面的可运行演示应用程序可能会完成这项工作.. .

  package random;

  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.TreeMap;

  public class Main {
      public static void main(String[] args) {
          Map hashMap = makeHashMap();
          // you can make any Map random by making them a RandomMap
          // better if you can just create the Map as a RandomMap instead of HashMap
          Map randomMap = new RandomMap(hashMap);

          // just call values() and iterate through them, they will be random
          Iterator iter = randomMap.values().iterator();

          while (iter.hasNext()) {
              String value = (String) iter.next();
              System.out.println(value);
          }
      }

      private static Map makeHashMap() {
          Map retVal;

          // HashMap is not ordered, and not exactly random (read the javadocs)
          retVal = new HashMap();

          // TreeMap sorts your map based on Comparable of keys
          retVal = new TreeMap();

          // RandomMap - a map that returns stuff randomly
          // use this, don't have to create RandomMap after function returns
          // retVal = new HashMap();

          for (int i = 0; i < 20; i++) {
              retVal.put("key" + i, "value" + i);
          }

          return retVal;
      }
  }

  /**
   * An implementation of Map that shuffles the Collection returned by values().
   * Similar approach can be applied to its entrySet() and keySet() methods.
   */
  class RandomMap extends HashMap {
      public RandomMap() {
          super();
      }

      public RandomMap(Map map) {
          super(map);
      }

      /**
       * Randomize the values on every call to values()
       *
       * @return randomized Collection
       */
      @Override
      public Collection values() {
          List randomList = new ArrayList(super.values());
          Collections.shuffle(randomList);

          return randomList;
      }

  }
于 2009-05-30T12:14:26.527 回答
0

我编写了一个实用程序来从映射、条目集或迭代器中检索随机条目、键或值。

由于您不能也不应该能够计算出迭代器的大小(Guava 可以做到这一点),您将不得不重载该randEntry()方法以接受应该是条目长度的大小。

package util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MapUtils {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>() {
            private static final long serialVersionUID = 1L;
            {
                put("Foo", 1);
                put("Bar", 2);
                put("Baz", 3);
            }
        };

        System.out.println(randEntryValue(map));
    }

    static <K, V> Entry<K, V> randEntry(Iterator<Entry<K, V>> it, int count) {
        int index = (int) (Math.random() * count);

        while (index > 0 && it.hasNext()) {
            it.next();
            index--;
        }

        return it.next();
    }

    static <K, V> Entry<K, V> randEntry(Set<Entry<K, V>> entries) {
        return randEntry(entries.iterator(), entries.size());
    }

    static <K, V> Entry<K, V> randEntry(Map<K, V> map) {
        return randEntry(map.entrySet());
    }

    static <K, V> K randEntryKey(Map<K, V> map) {
        return randEntry(map).getKey();
    }

    static <K, V> V randEntryValue(Map<K, V> map) {
        return randEntry(map).getValue();
    }
}
于 2016-02-23T18:41:41.567 回答
0

如果你对O(n)时间复杂度没问题,你可以使用类似values()or的方法,values().toArray()但是如果你寻找一个恒定的O(1) getRandom()操作,一个很好的选择是使用自定义数据结构。ArrayListHashMap可以组合以获得和的O(1)时间。这是一个示例实现:insert()remove()getRandom()

class RandomizedSet {
    List<Integer> nums = new ArrayList<>();
    Map<Integer, Integer> valToIdx = new HashMap<>();
    Random rand = new Random();

    public RandomizedSet() { }

    /**
     * Inserts a value to the set. Returns true if the set did not already contain
     * the specified element.
     */
    public boolean insert(int val) {
        if (!valToIdx.containsKey(val)) {
            valToIdx.put(val, nums.size());
            nums.add(val);
            return true;
        }
        return false;
    }

    /**
     * Removes a value from the set. Returns true if the set contained the specified
     * element.
     */
    public boolean remove(int val) {
        if (valToIdx.containsKey(val)) {
            int idx = valToIdx.get(val);
            int lastVal = nums.get(nums.size() - 1);

            nums.set(idx, lastVal);
            valToIdx.put(lastVal, idx);

            nums.remove(nums.size() - 1);
            valToIdx.remove(val);
            return true;
        }
        return false;
    }

    /** Get a random element from the set. */
    public int getRandom() {
        return nums.get(rand.nextInt(nums.size()));
    }
}

这个想法来自leetcode.com的这个问题。

于 2020-04-22T07:05:18.533 回答
0

似乎所有其他高票数的答案都迭代了所有元素。至少,这里不是所有元素都必须迭代:

Random generator = new Random();
return myHashMap.values().stream()
                .skip(random.nextInt(myHashMap.size()))
                .findFirst().get();
于 2021-12-15T15:06:04.367 回答
-1

这取决于您的密钥是什么 - 哈希图的性质不允许这种情况轻易发生。

我能想到的方法是选择一个介于 1 和哈希图大小之间的随机数,然后开始迭代它,随时保持计数 - 当计数等于你的随机数时选择,那是你的随机元素。

于 2009-05-30T11:10:41.617 回答