2009-05-30 12 views
24

¿Hay alguna manera de obtener el valor de un HashMap aleatoriamente en Java?¿Hay alguna manera de obtener el valor de un HashMap aleatoriamente en Java?

+0

¿Por qué lo necesita? Si se trata de algo más que las pruebas, está utilizando una estructura de datos incorrecta. Si es para probar, entonces probablemente no quieras datos aleatorios. – kdgregory

+0

Consulte mi respuesta a continuación: la forma adecuada de hacerlo depende un poco de sus circunstancias (especialmente la cantidad de elementos aleatorios que necesita por cada cambio en el mapa). –

+0

Si quiere algo eficiente, eche un vistazo a mi respuesta en http://stackoverflow.com/questions/9919734/selecting-random-key-and-value-sets-from-a-map-in-java/20724193# 20724193 Las tablas hash son en principio una gran estructura para acceder a elementos aleatorios. Es lamentable que la API de Java no nos proporcione una forma fácil de hacerlo. –

Respuesta

39

Se podría utilizar algo como:

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

actualización.

Lo anterior no funciona, Set.toArray() siempre devuelve una matriz de Object s, que no se puede forzar en una matriz de Map.Entry.

Este (versión simple) hace el trabajo:

Random generator = new Random(); 
Object[] values = myHashMap.values().toArray(); 
Object randomValue = values[generator.nextInt(values.length)]; 

Si desea que el valor aleatorio para ser un tipo que no sea una Object simplemente añadir un yeso para la última línea. Así que si myHashMap fue declarado como:

Map<Integer,String> myHashMap = new HashMap<Integer,String>(); 

La última línea puede ser:

String randomValue = (String) values[generator.nextInt(value.length)]; 
+1

al ejecutar su código Tom Obtuve la excepción "tipos incompatibles: encontrado java.util.object [] pero esperado java.util.entry []" – Varuna

+0

¿Cómo hacer el tipo necesario de conversión? He intentado hacer Map, Map.Entry el mensaje que se muestra es inconvertible tipos – Varuna

+0

¡Hola, Varuna, estás en lo cierto, ese código estaba roto! He agregado un código a mi respuesta que funciona. –

-1

Depende de cuál sea tu clave: la naturaleza de un hashmap no permite que esto ocurra fácilmente.

La forma en que puedo pensar en la parte superior de mi cabeza es seleccionar un número aleatorio entre 1 y el tamaño del hashmap, y luego empezar a iterar sobre él, manteniendo un conteo sobre la marcha - cuando el conteo es igual a ese número aleatorio que elijas, ese es tu elemento aleatorio.

+0

Kazer La clave y el valor I en mi mapa son de tipo String, es en esta situación que he enfrentado este problema. Hay un método value() que devuelve una vista de colecciones según la documentación, ¿es posible? a través de esto para tomar solo los valores de HashMap (no las claves) y obtener un valor aleatorio? – Varuna

+0

Por supuesto, aunque esto plantea la pregunta de por qué está utilizando un mapa hash en primer lugar ... –

3

Genere un número aleatorio entre 0 y la cantidad de claves en su HashMap. Obtenga la clave en el número al azar. Obtenga el valor de esa clave.

Pseudocódigo:

int n = random(map.keys().length()); 
String key = map.keys().at(n); 
Object value = map.at(key); 

Si es difícil de implementar esto en Java, entonces se podría crear y matriz a partir de este código usando la función toArray() en Set.

Object[] values = map.values().toArray(new Object[map.size()]); 
Object random_value = values[random(values.length)]; 

No estoy seguro de cómo hacer el número aleatorio.

+2

excepto que no se puede traducir a Java, ya que las teclas de un mapa son un conjunto, y los conjuntos no tienen ningún sentido de posición – kdgregory

+1

Creo que mi segunda idea funcionará mejor. –

+0

Eliminando el downvote porque buscó los métodos correctos (aunque todavía creo que el OP necesita repensar el problema). Eche un vistazo a java.lang.Random para el número aleatorio. – kdgregory

27

Dado que los requisitos sólo pide un valor aleatorio de la HashMap, aquí es el enfoque:

  1. El HashMap tiene un método values que devuelve Collection de los valores en el mapa.
  2. El Collection se utiliza para crear un List.
  3. El método size se utiliza para buscar el tamaño de List, que se utiliza con el método Random.nextInt para obtener un índice aleatorio de List.
  4. Finalmente, el valor se recupera del método Listget con el índice aleatorio.

Implementación:

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

Lo bueno de este enfoque es que todos los métodos son generic - no hay necesidad de encasillamiento.

+1

Estimado coobird, su código es correcto y funciona. Pero elegí lo anterior por Tom, ya que me parece que ¡¡¡¡¡más simple!!!!! Gracias por su ayuda – Varuna

0

Aquí es un ejemplo de cómo utilizar las matrices enfoque descrito por Peter Stuifzand, también a través de la 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)); 
+1

Esto devuelve una clave aleatoria y valor, pero, ¡no un par que es una asignación de clave-valor en el mapa! –

+0

Sí, pero el ejemplo fue para mostrar cómo aleatorizar las claves y los valores (consulte el comentario sobre los PO de la publicación anterior). :) Sin embargo, aclararé el ejemplo. – MH114

+0

("publicar por encima" significa Kazar, el pedido cambió ...;) – MH114

0

realmente no sé qué quiere hacer esto ... pero si ayuda, he creado un RandomMap que cambia aleatoriamente automáticamente los valores al llamar valores(), entonces la siguiente aplicación de demostración ejecutable podría hacer el trabajo ...

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

    } 
8

caso de que necesite para dibujar los valores de la futher mapa sin repetir ningún elemento puede poner la ma p en una lista y luego baraja.

List<Object> valuesList = new ArrayList<Object>(map.values()); 
Collections.shuffle(valuesList); 

for (Object obj : valuesList) { 
    System.out.println(obj); 
} 
+0

+1 por Collections.shuffle(); No había visto eso antes. Guay; ¡Gracias! –

1

Una buena respuesta depende ligeramente de las circunstancias, en particular, la frecuencia con la que necesita para obtener una clave aleatoria para un mapa determinado (N. B. la técnica es esencialmente la misma ya sea que tome clave o valor).

  • Si necesita varias claves aleatorias de un mapa dado, sin el mapa cambiar de entre conseguir las claves aleatorias , a continuación, utilizar el random sampling method como iterar a través del juego de llaves. Efectivamente lo que se hace es iterar sobre el conjunto devuelto por conjunto de claves(), y en cada artículo calcular la probabilidad de querer tomar esa clave, dado lo muchos necesitará en general y el número que ha tomado por lo lejos. A continuación, generará un número aleatorio y verá si ese número es menor que la probabilidad . (N. B. Este método siempre funciona, incluso si sólo se necesita 1 llave, sino que simplemente no es necesariamente la forma más eficiente en ese caso.)
  • Las claves en un HashMap son efectivamente con el fin de pseudo-aleatorios ya. En un caso extremo donde única vez necesita una clave aleatoria para un determinado mapa posible, incluso se podría simplemente extraiga el primer elemento de la conjunto de claves().
  • En otros casos (donde sea necesitan múltiples claves aleatorias posibles para un mapa posible dada, o en el mapa cambiará entre usted tomar al azar teclas), que tienen esencialmente a crear o mantener una matriz/list de las claves desde las que selecciona una clave aleatoria .
+1

... excepto que el OP quería valores aleatorios, no las claves – kdgregory

+0

ah perdón perdí eso - lo mismo se aplica esencialmente, aunque si tomas la clave o el valor. –

-1

Este es un ejercicio del libro Objects First With Java. Es el siguiente.El pickDefaultResponse() genera una respuesta usando un ArrayList y obteniendo un entero generado al azar y usándolo como el índice del ArrayList. Esto funciona Pero lo que quería superar era llenar dos listas. Aquí están la instancia HashMap responseMap y ArrayList instance defaultResponses. Ambos deben llenarse por separado.

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); 
    } 
} 
+0

Regrese y realmente piense por qué existen esos dos objetos. En realidad, ejecute el programa y vea cómo se comporta cuando escribe diferentes valores. Porque esos dos objetos tienen propósitos muy diferentes. – kdgregory

+0

Y en el futuro, solo edite su publicación original. – kdgregory

+0

sí sí Lo que hice estuvo mal, ¡No me vino a la mente editar el original en ese momento, lo siento mucho! El mapa de respuestas (HashMap) sirve al usuario con respuestas de acuerdo con su entrada. Las respuestas predeterminadas (ArrayList) generan una respuesta utilizando un índice aleatorio, si el mapa de respuestas (HashMap) no responde al usuario con una respuesta. La entrada del el usuario es la clave de HashMap, si ya se asigna con alguna de las claves en el HashMap, se emite una respuesta, si no se invoca pickDefaultResponses() y se usa un índice generado aleatoriamente para obtener un valor de ArrayList. Varuna Varuna – Varuna

0

lo general, no quieren realmente aleatorio con valor de sino simplemente cualquier valor, y luego es agradable hacer esto:

Object selectedObj = null; 
for (Object obj : map.values()) { 
    selectedObj = obj; 
    break; 
} 
0

la conversión a una matriz y luego obtener el valor es demasiado lento cuando está en el camino caliente.

así que el conjunto (ya sea el conjunto de claves o keyValue) y hacer algo como:

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(); 
    } 
0

escribí una utilidad para recuperar una entrada al azar, clave, o el valor de un mapa, conjunto de entrada, o iterador.

Dado que no puede y no debe poder determinar el tamaño de un iterador (Guava can do this), tendrá que sobrecargar el método randEntry() para aceptar un tamaño que debe ser la longitud de las entradas.

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(); 
    } 
} 
Cuestiones relacionadas