有没有办法在 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()
总是返回Object
s的数组,不能强制转换成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
方法,它返回映射中值的Collection
。
Collection
用于创建List
。
size
方法用于查找List
的大小,Random.nextInt
方法用于获取List
的随机索引。
最后,使用随机索引从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);
这种方法的好处是所有方法都是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()
操作,一个很好的选择是使用自定义数据结构。 ArrayList
和 HashMap
可以组合在一起,以达到 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 的值?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法在 Java 中暂停和恢复 Files.walkFileTree?