Is there a way to get the value of a HashMap randomly in Java?
Is there a way to get the value of a HashMap randomly in Java?
This works:
Random generator = new Random();
Object[] values = myHashMap.values().toArray();
Object randomValue = values[generator.nextInt(values.length)];
If you want the random value to be a type other than an Object simply add a cast to the last line. So if myHashMap was declared as:
Map<Integer,String> myHashMap = new HashMap<Integer,String>();
The last line can be:
String randomValue = (String) values[generator.nextInt(value.length)];
The below doesn't work, Set.toArray() always returns an array of Objects, which can't be coerced into an array of Map.Entry.
Random generator = new Random();
Map.Entry[] entries = myHashMap.entrySet().toArray();
randomValue = entries[generator.nextInt(entries.length)].getValue();
Since the requirements only asks for a random value from the HashMap, here's the approach:
HashMap has a values method which returns a Collection of the values in the map.Collection is used to create a List.size method is used to find the size of the List, which is used by the Random.nextInt method to get a random index of the List.List get method with the random index.Implementation:
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);
The nice part about this approach is that all the methods are generic -- there is no need for typecasting.
Should you need to draw futher values from the map without repeating any elements you can put the map into a List and then shuffle it.
List<Object> valuesList = new ArrayList<Object>(map.values());
Collections.shuffle( valuesList );
for ( Object obj : valuesList ) {
System.out.println( obj );
}
Generate a random number between 0 and the number of keys in your HashMap. Get the key at the random number. Get the value from that key.
Pseudocode:
int n = random(map.keys().length());
String key = map.keys().at(n);
Object value = map.at(key);
If it's hard to implement this in Java, then you could create and array from this code using the toArray() function in Set.
Object[] values = map.values().toArray(new Object[map.size()]);
Object random_value = values[random(values.length)];
I'm not really sure how to do the random number.
A good answer depends slightly on the circumstances, in particular how often you need to get a random key for a given map (N.B. the technique is essentially the same whether you take key or value).
If you are using Java 8, findAny function in a pretty solution:
MyEntityClass myRandomlyPickedObject = myHashMap.values().stream().findAny();
Here is an example how to use the arrays approach described by Peter Stuifzand, also through the values()-method:
// 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) );
i really don't know why you want to do this... but if it helps, i've created a RandomMap that automatically randomizes the values when you call values(), then the following runnable demo application might do the job...
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;
}
}
If you are fine with O(n) time complexity you can use methods like values() or values().toArray() but if you look for a constant O(1) getRandom() operation one great alternative is to use a custom data structure. ArrayList and HashMap can be combined to attain O(1) time for insert(), remove() and getRandom(). Here is an example implementation:
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()));
}
}
The idea comes from this problem from leetcode.com.
It seems that all other high voted answers iterate over all the elements. Here, at least, not all elements must be iterated over:
Random generator = new Random();
return myHashMap.values().stream()
.skip(random.nextInt(myHashMap.size()))
.findFirst().get();
It depends on what your key is - the nature of a hashmap doesn't allow for this to happen easily.
The way I can think of off the top of my head is to select a random number between 1 and the size of the hashmap, and then start iterating over it, maintaining a count as you go - when count is equal to that random number you chose, that is your random element.