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

Posted

技术标签:

【中文标题】有没有办法在 Java 中随机获取 HashMap 的值?【英文标题】:Is there a way to get the value of a HashMap randomly in Java? 【发布时间】:2010-10-30 02:18:53 【问题描述】:

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

【问题讨论】:

为什么需要这个?如果不是测试,则说明您使用了错误的数据结构。如果是用于测试,那么您可能不想要随机数据。 在下面查看我的答案——执行此操作的适当方法在一定程度上取决于您的情况(特别是每次更改地图需要多少随机元素)。 如果您想要一些高效的东西,请查看我在@987654321 中的回答@ 哈希表原则上是访问随机元素的绝佳结构。不幸的是,Java api 并没有为我们提供一个简单的方法。 @kdgregory 嗨,我知道这是一个旧线程,但我想知道你为什么说它是错误的随机访问数据结构?谢谢 @RegUser - HashMap 被设计为通过键而不是值来访问。双向映射有一些用途,但您应该选择一个为此目的而设计的类,而不是试图强迫 HashMap 做一些它原本不打算做的事情。 【参考方案1】:

这行得通:

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的数组,不能强制转换成Map.Entry的数组。

Random generator = new Random();
Map.Entry[] entries = myHashMap.entrySet().toArray();
randomValue = entries[generator.nextInt(entries.length)].getValue();

【讨论】:

在执行你的代码时,Tom 我得到异常“不兼容的类型 - 找到 java.util.object[] 但预期 java.util.entry[]” 如何进行必要的类型转换?我试过Map,Map.Entry显示的消息是不可转换的类型 嗨 Varuna,你说的很对,代码被破坏了!我在我的答案中添加了一些有效的代码。 coobird 下面的帖子也是正确的,它有效但我选择这个作为正确的解决方案,因为它更简单 嗨,您可以编辑答案,使其不包含无效部分吗?这很混乱。【参考方案2】:

由于要求只要求来自HashMap 的随机值,因此方法如下:

    HashMap 有一个values 方法,它返回映射中值的CollectionCollection 用于创建Listsize方法用于查找List的大小,Random.nextInt方法用于获取List的随机索引。 最后,使用随机索引从Listget方法中检索到值。

实施:

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);

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

【讨论】:

亲爱的 coobird,您的代码是正确的,并且它有效但我选择了 Tom 的上述代码,因为在我看来它更简单!!!!!!感谢您的帮助【参考方案3】:

如果您需要在不重复任何元素的情况下从地图中提取更多值,您可以将地图放入列表中,然后对其进行随机播放。

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

for ( Object obj : valuesList ) 
    System.out.println( obj );

【讨论】:

+1 for Collections.shuffle();我以前没见过。凉爽的;谢谢!【参考方案4】:

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

伪代码

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

如果在 Java 中很难实现,那么您可以使用 Set 中的 toArray() 函数从这段代码创建和数组。

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

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

【讨论】:

除了不能翻译成 Java,因为 Map 的键是一个 Set,而 Set 没有任何位置感 删除反对票,因为您查找了正确的方法(尽管我仍然认为 OP 需要重新考虑问题)。查看 java.lang.Random 中的随机数。【参考方案5】:

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

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

    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();
    

【讨论】:

【参考方案6】:

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

如果您需要各种随机密钥 从给定的地图,没有地图 在获得之间变化 随机键,然后在迭代时使用 random sampling method 通过密钥集。有效地是什么 你做的是迭代集合 由 keySet() 返回,并在每个 项目计算概率 想拿那把钥匙,考虑到如何 许多您将需要整体和 到目前为止,您已使用的号码。然后 生成一个随机数,看看是否 该数字低于 可能性。 (注:此方法始终有效,即使您只需要 1 个密钥;在这种情况下,它不一定是最有效的方法。) HashMap 中的键是有效的 已经是伪随机顺序了。在一个 极端情况,您只会 曾经需要一个随机密钥 给定可能的地图,你甚至可以 拉出的第一个元素 keySet(). 在其他情况(您要么 需要多个可能的随机密钥 对于给定的可能地图,或地图 会在你随机改变 键),你基本上必须 创建或维护一个数组/列表,您可以从中选择一个 随机密钥。

【讨论】:

... 除了 OP 想要随机值,而不是键 很抱歉错过了这一点 - 本质上同样适用,但无论您使用键还是值。【参考方案7】:

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

Object selectedObj = null;
for (Object obj : map.values()) 
    selectedObj = obj;
    break;

【讨论】:

问题是关于获取随机元素。不是第一个。【参考方案8】:

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

MyEntityClass myRandomlyPickedObject = myHashMap.values().stream().findAny();

【讨论】:

这是不好的做法,因为它用线性迭代 O(n) 替换了读取 O(1) 的 HashMap。对于小型地图是可以接受的,但如果尺寸增加,你就注定要失败。简而言之:如果您要节省一两行代码,请不要进行便捷编码。从不。 所有其他高票数的答案都是线性的,它遍历所有元素。至少,这里不是所有元素都必须迭代。 进行了一些测试,似乎findAny() 并不总是随机化。【参考方案9】:

我真的不知道你为什么要这样做......但如果它有帮助,我创建了一个 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;
      

  

【讨论】:

【参考方案10】:

这是一个如何使用 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) );

【讨论】:

这会返回一个随机键和值,但不是映射中的键值对! 是的,但是这个例子是为了展示如何随机化键和值(参见上面帖子中的 OP 评论)。 :) 不过,我会澄清这个例子。 ("post above" 意思是 Kazar 的,顺序改变了.. ;)【参考方案11】:

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

由于您不能也不应该能够计算出迭代器的大小 (Guava can do this),您将不得不重载 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();
    

【讨论】:

【参考方案12】:

如果您对O(n) 时间复杂度没问题,您可以使用values()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()));
    

想法来自this 问题来自leetcode.com。

【讨论】:

【参考方案13】:

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

Random generator = new Random();
return myHashMap.values().stream()
                .skip(random.nextInt(myHashMap.size()))
                .findFirst().get();

【讨论】:

【参考方案14】:

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

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

【讨论】:

Kazer 我的地图中的键和值都是字符串类型,正是在这种情况下我遇到了这个问题。有一个 value() 方法根据返回集合视图文档中,是否可以通过这个仅获取 HashMap 值(而不是键)并获得随机值? 当然,尽管这会引发一个问题,即为什么您首先要使用哈希映射...

以上是关于有没有办法在 Java 中随机获取 HashMap 的值?的主要内容,如果未能解决你的问题,请参考以下文章

从集合中获取随机元素

有没有办法在 R 中获取用于随机森林树分类的实例?

有没有办法在 Java 中暂停和恢复 Files.walkFileTree?

如何在HashMap中存储一个带Gson的JSON

Java中HashMap和LinkedHashMap以及TreeMap的区别

数组380. 常数时间插入删除和获取随机元素