views:

1494

answers:

9

Is there a way to get the value of a HashMap randomly in Java?

A: 

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.

Kazar
Kazer The key and the value I in my map are both of type String, it is in this situation that I have faced this problem.There is a value() method which returns a collections view according to the documentation, is it possible through this to take only the HashMap values(not keys) and get a random value?
Varuna
Of course, though this then raises the question of why you're using a hash map in the first place...
Neil Coffey
+2  A: 

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.

Peter Stuifzand
except that's untranslatable to Java, since the keys of a Map are a Set, and Sets don't have any sense of position
kdgregory
I think my second idea will work better.
Peter Stuifzand
Removing the downvote because you looked up the correct methods (although I still think the OP needs to rethink the problem). Take a look at java.lang.Random for the random number.
kdgregory
+8  A: 

You could use something like:

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

Update.

The above doesn't work, Set.toArray() always returns an array of Objects, which can't be coerced into an array of Map.Entry.

This (simpler version) does work:

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)];
Tom
when executing your code Tom I get the exception "incompatible types-found java.util.object[] but expected java.util.entry[]"
Varuna
How to do the necessary type casting?I have tried to Map, Map.Entry the message displayed is inconvertible types
Varuna
Hi Varuna, you're quite correct, that code was broken! I've added some code to my answer that does work.
Tom
The post below this by coobird is also correct and it works But I choose this as the correct solution because this more simple
Varuna
Thanks for your help!!!!!!!!
Varuna
+8  A: 

Since the requirements only asks for a random value from the HashMap, here's the approach:

  1. The HashMap has a values method which returns a Collection of the values in the map.
  2. The Collection is used to create a List.
  3. 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.
  4. 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.

coobird
Dear coobird your code is correct is and it works But I choose the above by Tom since it appears to me as more simple!!!!!Thanks for your help
Varuna
A: 

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) );
MH114
This returns a random key and value, but, not a pair that is a key-value mapping in the map!
Sean Owen
Yes, but the example was to show how to randomize keys and values (see OPs comment from the post above this). :) I will clarify the example, though.
MH114
("post above" meaning Kazar's, the order changed.. ;)
MH114
A: 

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

  }
koss
+2  A: 

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 );
}
banjollity
+1 for Collections.shuffle(); I hadn't seen that before. Cool; thanks!
Carl Manaster
A: 

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.
Neil Coffey
... except that the OP wanted random values, not keys
kdgregory
ah sorry missed that -- the same essentially applies, though whether you take the key or value.
Neil Coffey
A: 

This is an exercise from the book Objects First With Java.It is as below.The pickDefaultResponse() generates a response using an ArrayList and getting an randomly generated integer and using it as the index of the ArrayList.This works But what I wanted to overcome was filling up two Lists.Here the HashMap instance responseMap as well as ArrayList instance defaultResponses both has to be filled up separately.

import java.util.HashMap;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

/**
 * The responder class represents a response generator object.
 * It is used to generate an automatic response, based on specified input.
 * Input is presented to the responder as a set of words, and based on those
 * words the responder will generate a String that represents the response.
 *
 * Internally, the reponder uses a HashMap to associate words with response
 * strings and a list of default responses. If any of the input words is found
 * in the HashMap, the corresponding response is returned. If none of the input
 * words is recognized, one of the default responses is randomly chosen.
 * 
 * @version    1.0
 * @author     Michael Kolling and David J. Barnes
 */
public class Responder
{
    // Used to map key words to responses.
    private HashMap<String, String> responseMap;
    // Default responses to use if we don't recognise a word.
    private ArrayList<String> defaultResponses;
    private Random randomGenerator;

    /**
     * Construct a Responder
     */
    public Responder()
    {
        responseMap = new HashMap<String, String>();
        defaultResponses = new ArrayList<String>();
        fillResponseMap();
        fillDefaultResponses();
        randomGenerator = new Random();
    }

    /**
     * Generate a response from a given set of input words.
     * 
     * @param words  A set of words entered by the user
     * @return       A string that should be displayed as the response
     */
    public String generateResponse(HashSet<String> words)
    {
        Iterator<String> it = words.iterator();
        while(it.hasNext()) {
            String word = it.next();
            String response = responseMap.get(word);
            if(response != null) {
                return response;
            }
        }
        // If we get here, none of the words from the input line was recognized.
        // In this case we pick one of our default responses (what we say when
        // we cannot think of anything else to say...)
        return **pickDefaultResponse();**
    }

    /**
     * Enter all the known keywords and their associated responses
     * into our response map.
     */
    private void fillResponseMap()
    {
        responseMap.put("crash", 
                        "Well, it never crashes on our system. It must have something\n" +
                        "to do with your system. Tell me more about your configuration.");
        responseMap.put("crashes", 
                        "Well, it never crashes on our system. It must have something\n" +
                        "to do with your system. Tell me more about your configuration.");
        responseMap.put("slow", 
                        "I think this has to do with your hardware. Upgrading your processor\n" +
                        "should solve all performance problems. Have you got a problem with\n" +
                        "our software?");
        responseMap.put("performance", 
                        "Performance was quite adequate in all our tests. Are you running\n" +
                        "any other processes in the background?");
        responseMap.put("bug", 
                        "Well, you know, all software has some bugs. But our software engineers\n" +
                        "are working very hard to fix them. Can you describe the problem a bit\n" +
                        "further?");
        responseMap.put("buggy", 
                        "Well, you know, all software has some bugs. But our software engineers\n" +
                        "are working very hard to fix them. Can you describe the problem a bit\n" +
                        "further?");
        responseMap.put("windows", 
                        "This is a known bug to do with the Windows operating system. Please\n" +
                        "report it to Microsoft. There is nothing we can do about this.");
        responseMap.put("macintosh", 
                        "This is a known bug to do with the Mac operating system. Please\n" +
                        "report it to Apple. There is nothing we can do about this.");
        responseMap.put("expensive", 
                        "The cost of our product is quite competitive. Have you looked around\n" +
                        "and really compared our features?");
        responseMap.put("installation", 
                        "The installation is really quite straight forward. We have tons of\n" +
                        "wizards that do all the work for you. Have you read the installation\n" +
                        "instructions?");
        responseMap.put("memory", 
                        "If you read the system requirements carefully, you will see that the\n" +
                        "specified memory requirements are 1.5 giga byte. You really should\n" +
                        "upgrade your memory. Anything else you want to know?");
        responseMap.put("linux", 
                        "We take Linux support very seriously. But there are some problems.\n" +
                        "Most have to do with incompatible glibc versions. Can you be a bit\n" +
                        "more precise?");
        responseMap.put("bluej", 
                        "Ahhh, BlueJ, yes. We tried to buy out those guys long ago, but\n" +
                        "they simply won't sell... Stubborn people they are. Nothing we can\n" +
                        "do about it, I'm afraid.");
    }

    /**
     * Build up a list of default responses from which we can pick one
     * if we don't know what else to say.
     */
    private void fillDefaultResponses()
    {
        defaultResponses.add("That sounds odd. Could you describe that problem in more detail?");
        defaultResponses.add("No other customer has ever complained about this before. \n" +
                             "What is your system configuration?");
        defaultResponses.add("That sounds interesting. Tell me more...");
        defaultResponses.add("I need a bit more information on that.");
        defaultResponses.add("Have you checked that you do not have a dll conflict?");
        defaultResponses.add("That is explained in the manual. Have you read the manual?");
        defaultResponses.add("Your description is a bit wishy-washy. Have you got an expert\n" +
                             "there with you who could describe this more precisely?");
        defaultResponses.add("That's not a bug, it's a feature!");
        defaultResponses.add("Could you elaborate on that?");
    }

    /**
     * Randomly select and return one of the default responses.
     * @return     A random default response
     */
    private String **pickDefaultResponse()**
    {
        // Pick a random number for the index in the default response list.
        // The number will be between 0 (inclusive) and the size of the list (exclusive).
        int index = randomGenerator.nextInt(defaultResponses.size());
        return defaultResponses.get(index);
    }
}
Varuna
Go back and really think about why those two objects exist. Actually run the program and see how it behaves when you type different values. Because those two objects have very different purposes.
kdgregory
And in the future, please just edit your original posting.
kdgregory
yes yes What I did was wrong, It didn't come to me to edit the original at the time, sorry about that!!.The responseMap(HashMap) serves the user with responses according to his/her input.The defaultResponses(ArrayList) generates a reponse using an random index, if the responseMap(HashMap) does not serve the user with a response.The input by the user is the key to the HashMap, If it maps with any of the keys in the HashMap already, a response is issued else pickDefaultResponses() is invoked and randomly generated index is used to get a value from the ArrayList.VarunaVaruna
Varuna
@Varuna: So edit this into your OP now and delete this answer.
Software Monkey
@Software Monkey: If I edit OP and delete this what will happen to the comments, that kdgregory added with the intention of helping and teaching me things around here!!!
Varuna