有没有办法在 Java 中随机获取 HashMap 的值?
14 回答
这有效:
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 的数组,它不能被强制转换成一个 s 的数组Map.Entry
。
Random generator = new Random();
Map.Entry[] entries = myHashMap.entrySet().toArray();
randomValue = entries[generator.nextInt(entries.length)].getValue();
由于要求仅要求从 中获取随机值,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);
这种方法的好处是所有方法都是通用的——不需要类型转换。
如果您需要在不重复任何元素的情况下从地图中绘制更多值,您可以将地图放入 List 中,然后对其进行洗牌。
List<Object> valuesList = new ArrayList<Object>(map.values());
Collections.shuffle( valuesList );
for ( Object obj : valuesList ) {
System.out.println( obj );
}
生成一个介于 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)];
我不太确定如何做随机数。
当它在热路径中时,将其转换为数组然后获取值太慢了。
所以获取集合(键或键值集)并执行以下操作:
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();
}
一个好的答案稍微取决于具体情况,特别是您需要多长时间为给定地图获取随机密钥(注意,无论您获取密钥还是值,该技术本质上都是相同的)。
- 如果您需要 来自给定映射的各种随机键,并且在获取随机键之间没有更改映射,则在遍历键集时使用随机采样方法。实际上,您所做的是遍历 keySet() 返回的集合,并在每个项目上计算想要获取该密钥的概率,考虑到您总共需要多少以及到目前为止您已经获取的数量。然后生成一个随机数,看看这个数字是否低于概率。(注意,即使您只需要 1 个密钥,此方法也将始终有效;在这种情况下,它不一定是最有效的方法。)
- HashMap 中的键实际上已经是伪随机顺序。在极端情况下,对于给定的可能映射,您只需要一个随机键,您甚至可以只提取keySet() 的第一个元素。
- 在其他情况下(对于给定的可能映射,您需要多个可能的随机键,或者在您使用随机键之间映射会发生变化),您基本上必须 创建或维护一个键的数组/列表,从中选择一个随机键钥匙。
通常你并不真正想要一个随机值,而只是想要任何值,然后这样做很好:
Object selectedObj = null;
for (Object obj : map.values()) {
selectedObj = obj;
break;
}
如果您使用的是 Java 8,请使用findAny
一个漂亮的解决方案:
MyEntityClass myRandomlyPickedObject = myHashMap.values().stream().findAny();
下面是一个示例,如何使用 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) );
我真的不知道你为什么要这样做......但如果它有帮助,我已经创建了一个 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;
}
}
我编写了一个实用程序来从映射、条目集或迭代器中检索随机条目、键或值。
由于您不能也不应该能够计算出迭代器的大小(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();
}
}
如果你对O(n)
时间复杂度没问题,你可以使用类似values()
or的方法,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()));
}
}
这个想法来自leetcode.com的这个问题。
似乎所有其他高票数的答案都迭代了所有元素。至少,这里不是所有元素都必须迭代:
Random generator = new Random();
return myHashMap.values().stream()
.skip(random.nextInt(myHashMap.size()))
.findFirst().get();
这取决于您的密钥是什么 - 哈希图的性质不允许这种情况轻易发生。
我能想到的方法是选择一个介于 1 和哈希图大小之间的随机数,然后开始迭代它,随时保持计数 - 当计数等于你的随机数时选择,那是你的随机元素。