Join two collections and get a random element - java

I need to join two Collection<String>, get n random elements and remove them from the original collection in which they are stored.
To join the collections I thought about iterate them and store in an custom map structure in a way to:
have the same key stored n times
get the original collection.
Is there a simple method to do that?
Can you help me?

How about this:
Collection<String> collection1 = new ArrayList<String>();
Collection<String> collection2 = new ArrayList<String>();
List<String> allElements = new ArrayList<String>(collection1);
allElements.addAll(collection2);
Collections.shuffle(allElements);
Random random = new Random();
int n = 10;
List<String> randomResults = new ArrayList<String>(n);
for (int i = 0; i < n && !allElements.isEmpty(); i++) {
String randomElement = allElements.remove(random.nextInt(allElements.size()));
randomResults.add(randomElement);
}
collection1.removeAll(randomResults);
collection2.removeAll(randomResults);

Interesting you want to use the map for that. I would suggest using a MultiMap (Google's Guava for example). It allows you to hold a key and then a Collection of values, all belonging to that key. So, you would have only two keys (corresponding to your original Collections).
Another solution would be to just add all Collections into a third a Collection (there is an addAll(Collection c) method available). Provided, there are no duplicate values, you can check if a certain item from the third collection is part of any of your other two while iterating.
These are kind of rudimentary ways to achieve whatever your question asked, but worth a try.
Hope these pointers help a bit!

Does the following work for you?
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class Combiner {
private static final Random rnd = new Random();
public static void main(String[] args) {
Collection<String> boys = Sets.newHashSet("James", "John", "andrew",
"peter");
Collection<String> girls = Sets.newHashSet("mary", "jane", "rose",
"danny");
// Combine the two
Iterable<String> humans = Iterables.concat(boys, girls);
// Get n Random elements from the mix
int n = 2;
Collection<String> removed = randomSample4(humans, n);
// Remove from the original Collections
Iterables.removeAll(boys, removed);
Iterables.removeAll(girls, removed);
// or even
boys.removeAll(removed);
girls.removeAll(removed);
// And now we check if all is well
System.out.println(boys);
System.out.println(girls);
}
public static <T> Collection<T> randomSample4(Iterable<T> humans, int m) {
List<T> sample = Lists.newArrayList(humans);
Set<T> res = new HashSet<T>(m);
int n = sample.size();
for (int i = n - m; i < n; i++) {
int pos = rnd.nextInt(i + 1);
T item = sample.get(pos);
if (res.contains(item))
res.add(sample.get(i));
else
res.add(item);
}
return res;
}
}
The randomSample4 method has been copied from this blog.

Related

How to find the max number of an unique string element in a alphanumeric Array list in java

I have list that has alphanumeric elements. I want to find the maximum number of each elements individually.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Collect {
public static void main(String[] args) {
List<String> alphaNumericList = new ArrayList<String>();
alphaNumericList.add("Demo.23");
alphaNumericList.add("Demo.1000");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Test.01");
alphaNumericList.add("Test.02");
alphaNumericList.add("Test.100");
alphaNumericList.add("Test.99");
Collections.sort(alphaNumericList);
System.out.println("Output "+Arrays.asList(alphaNumericList));
}
I need filter only below values. For that I am sorting the list but it filters based on the string rather than int value. I want to achieve in an efficient way. Please suggest on this.
Demo.1000
Test.100
Output [[Demo.1000, Demo.12, Demo.12, Demo.23, Test.01, Test.02, Test.100, Test.99]]
You can either create a special AlphaNumericList type, wrapping the array list or whatever collection(s) you want to use internally, giving it a nice public interface to work with, or for the simplest case if you want to stick to the ArrayList<String>, just use a Comparator for sort(..):
package de.scrum_master.stackoverflow.q60482676;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.lang.Integer.parseInt;
public class Collect {
public static void main(String[] args) {
List<String> alphaNumericList = Arrays.asList(
"Demo.23", "Demo.1000", "Demo.12", "Demo.12",
"Test.01", "Test.02", "Test.100", "Test.99"
);
Collections.sort(
alphaNumericList,
(o1, o2) ->
((Integer) parseInt(o1.split("[.]")[1])).compareTo(parseInt(o2.split("[.]")[1]))
);
System.out.println("Output " + alphaNumericList);
}
}
This will yield the following console log:
Output [Test.01, Test.02, Demo.12, Demo.12, Demo.23, Test.99, Test.100, Demo.1000]
Please let me know if you don't understand lambda syntax. You can also use an anonymous class instead like in pre-8 versions of Java.
Update 1: If you want to refactor the one-line lambda for better readability, maybe you prefer this:
Collections.sort(
alphaNumericList,
(text1, text2) -> {
Integer number1 = parseInt(text1.split("[.]")[1]);
int number2 = parseInt(text2.split("[.]")[1]);
return number1.compareTo(number2);
}
);
Update 2: If more than one dot "." character can occur in your strings, you need to get the numeric substring in a different way via regex match, still not complicated:
Collections.sort(
alphaNumericList,
(text1, text2) -> {
Integer number1 = parseInt(text1.replaceFirst(".*[.]", ""));
int number2 = parseInt(text2.replaceFirst(".*[.]", ""));
return number1.compareTo(number2);
}
);
Update 3: I just noticed that for some weird reason you put the sorted list into another list via Arrays.asList(alphaNumericList) when printing. I have replaced that by just alphaNumericList in the code above and also updated the console log. Before the output was like [[foo, bar, zot]], i.e. a nested list with one element.
Check below answer:
public static void main(String[] args) {
List<String> alphaNumericList = new ArrayList<String>();
alphaNumericList.add("Demo.23");
alphaNumericList.add("Demo.1000");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Test.01");
alphaNumericList.add("Test.02");
alphaNumericList.add("Test.100");
alphaNumericList.add("Test.99");
Map<String, List<Integer>> map = new HashMap<>();
for (String val : alphaNumericList) {
String key = val.split("\\.")[0];
Integer value = Integer.valueOf(val.split("\\.")[1]);
if (map.containsKey(key)) {
map.get(key).add(value);
} else {
List<Integer> intList = new ArrayList<>();
intList.add(value);
map.put(key, intList);
}
}
for (Map.Entry<String, List<Integer>> entry : map.entrySet()) {
List<Integer> valueList = entry.getValue();
Collections.sort(valueList, Collections.reverseOrder());
System.out.print(entry.getKey() + "." + valueList.get(0) + " ");
}
}
Using stream and toMap() collector.
Map<String, Long> result = alphaNumericList.stream().collect(
toMap(k -> k.split("\\.")[0], v -> Long.parseLong(v.split("\\.")[1]), maxBy(Long::compare)));
The result map will contain word part as a key and maximum number as a value of the map(in your example the map will contain {Demo=1000, Test=100})
a. Assuming there are string of type Demo. and Test. in your arraylist.
b. It should be trivial to filter out elements with String Demo. and then extract the max integer for same.
c. Same should be applicable for extracting out max number associated with Test.
Please check the following snippet of code to achieve the same.
Set<String> uniqueString = alphaNumericList.stream().map(c->c.replaceAll("\\.[0-9]*","")).collect(Collectors.toSet());
Map<String,Integer> map = new HashMap<>();
for(String s:uniqueString){
int max= alphaNumericList.stream().filter(c -> c.startsWith(s+".")).map(c -> c.replaceAll(s+"\\.","")).map(c-> Integer.parseInt(c)).max(Integer::compare).get();
map.put(s,max);
}

Combinations of attributes values

I struggle with generating all possible combinations of values of a List of Attributes. As an example, for three attributes A, B,C, with the following values:{a1,a2} for A ,{b1,b2} for B, and {c1,c2} for C, I should get 8 combinations:
a1,b1,c1
a1,b1,c2
a1,b2,c1
a1,b2,c2
a2,b1,c1
a2,b1,c2
a2,b2,c1
a2,b2,c2
I used the following two recursive java functions where attribute_to_domain is a Map where we put each attribute as a key and its values as a value, and we add each combination as an <ArrayList<String> toenumerate_tuples as an ArrayList<ArrayList<String>>
public void fillTuples(Map<String, Set<String>> attribute_to_domain, ArrayList<String> attributes, ArrayList<ArrayList<String>> enumerate_tuples)
{
for (Map.Entry<String, Set<String>> entrySet :attribute_to_domain.entrySet()) {
String attribute=entrySet.getKey();
attributes.add(attribute);
}
int pos = 0;
Set<String> domain = attribute_to_domain.get(attributes.get(pos));
for (Iterator<String> it = domain.iterator(); it.hasNext();) {
String val = it.next();
ArrayList<String> tuple=new ArrayList<String>();
tuple.add(val);
fillTuples(attribute_to_domain, attributes, 1, tuple, enumerate_tuples);
tuple.remove(tuple.size()-1);
assert(tuple.isEmpty());
}
}
public void fillTuples(Map<String, Set<String>> attribute_to_domain, ArrayList<String> attributes, int pos, ArrayList<String> tuple, ArrayList<ArrayList<String>> enumerate_tuples)
{
assert(tuple.size() == pos);
if (pos == attributes.size())
{
enumerate_tuples.add(tuple);
return;
}
Set<String> domain = attribute_to_domain.get(attributes.get(pos));
for (Iterator<String> it = domain.iterator(); it.hasNext();) {
String val = it.next();
tuple.add(val);
fillTuples(attribute_to_domain, attributes, pos+1, tuple, enumerate_tuples);
tuple.remove(tuple.size()-1);
}
}
The problem that I get enumerate_tuples with empty elements and I can not keep changes that happened on it through the calls.
How can I solve this problem, please? Thanks in advance.
There is a simpler and faster solution, one that does not require recursion.
The number of output combinations can be calculated in advanced: multiplication of attributes in your case 2*2*2 but it is true for every combination.
Furthermore, we can calculate which value will be placed in each combination based on the combination index. if we assume combination index goes from 0 to 7:
for A:
- combinations 0-3 will contain a1
- combinations 4-7 will contain a2
for B
- combinations 0,1,4,5 will contain b1
- combinations 2,3,6,7 will contain b2
for C
- combinations 0,2,4,6 will contain c1
- combinations 1,3,5,7 will contain c2
so the formula for value placement is based on the combination index, the order of the attributes (A first etc) and the order of the values in the attribute.
the complexity of this algorithm is o(n*m) where n is number of attributes and m number of values.
Revised from Cartesian product of arbitrary sets in Java
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public class CartesianProduct {
public static Set<Set<Object> > cartesianProduct(Set<?>... sets) {
if (sets.length < 2)
throw new IllegalArgumentException(
"Can't have a product of fewer than two sets (got " +
sets.length + ")");
return _cartesianProduct(0, sets);
}
private static Set<Set<Object> > _cartesianProduct(int index, Set<?>... sets) {
Set<Set<Object> > ret = new TreeSet<Set<Object> >(new Comparator<Set<Object> >() {
#Override
public int compare(Set<Object> o1, Set<Object> o2) {
return o1.toString().compareTo(o2.toString());
}
});
if (index == sets.length) {
ret.add(new TreeSet<Object>());
} else {
for (Object obj : sets[index]) {
for (Set<Object> set : _cartesianProduct(index+1, sets)) {
set.add(obj);
ret.add(set);
}
}
}
return ret;
}
public static void main(String[] args) {
Map<String, Set<String> > dataMap = new HashMap<String, Set<String> >();
dataMap.put("A", new TreeSet<String>(Arrays.asList("a1", "a2")));
dataMap.put("B", new TreeSet<String>(Arrays.asList("b1", "b2")));
dataMap.put("C", new TreeSet<String>(Arrays.asList("c1", "c2")));
System.out.println(cartesianProduct(dataMap.values().toArray(new Set<?>[0])));
}
}

How to link elements of two linked list in java?

This is my starting code for a van rental database.
List<String> manual = new LinkedList<>();
List<String> automatic = new LinkedList<>();
List<String> location = new LinkedList<>();
manual.add("Queen");
manual.add("Purple");
manual.add("Hendrix");
automatic.add("Wicked");
automatic.add("Zeppelin");
automatic.add("Floyd");
automatic.add("Ramones");
automatic.add("Nirvana");
location.add("CBD");
location.add("Penrith");
location.add("Ceremorne");
location.add("Sutherland");
How can I link the cars to the location.
For example, location CBD has Wicked,Zepplin and Floyd, and Penrith has Queen.
So if the command line arguement has "Print CBD" then it must show the vans available in CBD.
Any help will be appreciated.
This is hardly a database. They are just three separate data pieces. Use some object-oriented design technique to create classes, such as a class called Van. For example, it's not java code exactly, just for example.
Class Van {
string name;
VanType type; // e.x, Enum {auto, manual}
Location location; // another class
}
I think you would be better off using the approach explained In This Post. I believe this would be a much clearer implementation.
I hope this helps.
Ok thats the code.
We are using only linked list as you wanted.
(linked list keeps track on the input order so we are using that too)
As it is one to many relation we should have some kind of "foreign key" so we can see the related object. For each car you add no matter manual or auto, you should add a key for the location as you can see below
for example rels[0] = 3; means that your first car will have relation with 4th object of the locations list. thats implemented in the code - take a look.
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class TestMain {
public static void main(String[] args) {
List<String> manual = new LinkedList<String>();
List<String> automatic = new LinkedList<String>();
List<String> location = new LinkedList<String>();
int[] rels = new int[8];
//cars with relations
rels[0] = 1;
manual.add("Queen");
rels[1] = 1;
manual.add("Purple");
rels[2] = 1;
manual.add("Hendrix");
rels[3] = 1;
automatic.add("Wicked");
rels[4] = 0;
automatic.add("Zeppelin");
rels[5] = 0;
automatic.add("Floyd");
rels[6] = 1;
automatic.add("Ramones");
rels[7] = 2;
automatic.add("Nirvana");
//key-0
location.add("CBD");
//key-1
location.add("Penrith");
//key-2
location.add("Ceremorne");
//key-3
location.add("Sutherland");
//here is the value that you have from your input args[] for example
String desiredLocation = "CBD";
int index = getLocationIndex(location, desiredLocation);
//if desired location not found we will print nothing
if(index==-1)return;
List mergedCars = new LinkedList<String>();
mergedCars.addAll(manual);
mergedCars.addAll(automatic);
for (int i = 0; i < rels.length; i++) {
if(index == rels[i])
{
System.out.println(mergedCars.get(i));
}
}
}
private static int getLocationIndex(List<String> location, String desiredLocation) {
int counter=0;
for (Iterator iterator = location.iterator(); iterator.hasNext();) {
String temp = (String) iterator.next();
if(temp.equals(desiredLocation))
{
return counter;
}
counter++;
}
return -1;
}
}

Algorithm to map one key values to another key's values in the Map

I have some Maps which I would like to calculate the cartesian product of. Can someone please suggest a good algorithm:
Data:
Key1 {100,101,102}
Key2 {200,201}
Key3 {300}
Required Output: (Order does matter)
100,200,300
101,200,300
102,200,300
100,201,300
101,201,300
102,201,300
Map is dynamic so Key and values can vary in size.
Thanks.
You will want to switch to using a LinkedHashMap so that order is preserved when you're iterating over keys.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class CartesianPrint {
public static void main(String[] args) {
Map<Integer,List<Integer>> groupMap = new LinkedHashMap<Integer,List<Integer>>();
groupMap.put(1,Arrays.asList(new Integer[]{100,101,102}));
groupMap.put(2,Arrays.asList(new Integer[]{200,201}));
groupMap.put(3,Arrays.asList(new Integer[]{300}));
List<List<Integer>> values = new ArrayList<List<Integer>>(groupMap.values());
int[] printList = new int[values.size()];
print(values,printList,values.size()-1);
}
static void print(List<List<Integer>> values, int[] printList, int level){
for (Integer value: values.get(level)) {
printList[level] = value;
if(level == 0){
System.out.println(Arrays.toString(printList));
}else{
print(values,printList,level-1);
}
}
}
}
Same as Ondra Žižka, if you don't need a map, take a List it works the same way.
Here is a not so optimized way (I should clone instead of recalculating product in recursion. But the idea is still here and its pretty short. I took special care to keep correct order, that's why I run through List backwards.
public static List<List<Integer>> getCart(List<List<Integer>> a_list) {
List<List<Integer>> l_result = new ArrayList<List<Integer>>();
if (a_list == null || a_list.isEmpty()) {
l_result.add(new ArrayList<Integer>());
return l_result;
}
for (Integer l_value : a_list.get(a_list.size()-1)) {
List<List<Integer>> l_resultPortion = getCart(a_list.subList(0, a_list.size() - 1));
for (List<Integer> l_list : l_resultPortion) {
l_list.add(l_value);
}
l_result.addAll(l_resultPortion);
}
return l_result;
}
I suggest to create a store of tuples (triplet in your example).
List<List<Integer>> store = new LinkedList();
Then create a Stack of numbers.
Stack<Integer> stack = new Stack();
Then write a recursive function:
In each recursive function call, push the actually processed value of the array into the stack, and add the current tuple to the store.
private static process( Iterator<String> keys ){
// Bottom-most key
if( ! keys.hasNext() ){
// Construct the tuple from the stack and add it to store.
}
else {
String currentKey = keys.next();
List<Integer> numbers = map.get( currentKey );
for( int i : numbers ){
stack.push( i );
process ( keys );
stack.pop(); // Dispose processed number.
}
}
}
I hope I figured out the problem right (no guarantee).
Sorry for not implementing it whole but that's your homework :)

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:
The HashMap has a values method which returns a Collection of the values in the map.
The Collection is used to create a List.
The 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.
Finally, the value is retrieved from the 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.
Converting it to an array and then getting the value is too slow when its in the hot path.
so get the set (either the key or keyvalue set) and do something like:
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();
}
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 need various random keys
from a given map, without the map
changing in between getting the
random keys, then use the random
sampling method as you iterate
through the key set. Effectively what
you do is iterate over the set
returned by keySet(), and on each
item calculate the probability of
wanting to take that key, given how
many you will need overall and the
number you've taken so far. Then
generate a random number and see if
that number is lower than the
probability. (N.B. This method will always work, even if you only need 1 key; it's just not necessarily the most efficient way in that case.)
The keys in a HashMap are effectively
in pseudo-random order already. In an
extreme case where you will only
ever need one random key for a
given possible map, you could even just
pull out the first element of the
keySet().
In other cases (where you either
need multiple possible random keys
for a given possible map, or the map
will change between you taking random
keys), you essentially have to
create or maintain an array/list of the keys from which you select a
random key.
If you are using Java 8, findAny function in a pretty solution:
MyEntityClass myRandomlyPickedObject = myHashMap.values().stream().findAny();
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;
}
}
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) );
Usually you do not really want a random value but rather just any value, and then it's nice doing this:
Object selectedObj = null;
for (Object obj : map.values()) {
selectedObj = obj;
break;
}
I wrote a utility to retrieve a random entry, key, or value from a map, entry set, or iterator.
Since you cannot and should not be able to figure out the size of an iterator (Guava can do this) you will have to overload the randEntry() method to accept a size which should be the length of the entries.
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();
}
}
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.

Categories

Resources