2009-05-30 16 views
24

C'è un modo per ottenere il valore di una HashMap in modo casuale in Java?C'è un modo per ottenere il valore di una HashMap in modo casuale in Java?

+0

Perché ne hai bisogno? Se si tratta di qualcosa di diverso dal test, stai usando la struttura dei dati sbagliata. Se è per test, allora probabilmente non vuoi dati casuali. – kdgregory

+0

Vedere la mia risposta qui sotto - il modo appropriato per farlo dipende un po 'dalle circostanze (in particolare quanti elementi casuali avete bisogno per modifica alla mappa). –

+0

Se vuoi qualcosa di efficiente, dai un'occhiata alla mia risposta in http://stackoverflow.com/questions/9919734/selecting-random-key-and-value-sets-from-a-map-in-java/20724193# 20724193 Gli hashtable sono in linea di principio una grande struttura per accedere a elementi casuali. È un peccato che l'API Java non ci dia un modo semplice per farlo. –

risposta

39

Si potrebbe usare qualcosa come:

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

Update.

che questo non funziona, Set.toArray() restituisce sempre una matrice di Object s, che non possono essere costrette in un array di Map.Entry.

Questo (versione semplificata) funziona:

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

Se si desidera che il valore casuale per essere un tipo diverso da un Object è sufficiente aggiungere un cast all'ultima riga. Quindi, se myHashMap è stato dichiarato come:

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

L'ultima riga può essere:

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

quando si esegue il codice di Tom ottengo l'eccezione "incompatibili tipi-trovati java.util.object [] ma java.util.entry previsto []" – Varuna

+0

Come fare il casting di tipo necessario? Mi hanno cercato di Mappa, Map.Entry il messaggio visualizzato è di tipo inconvertibile – Varuna

+0

Ciao Varuna, hai ragione, quel codice era rotto! Ho aggiunto del codice alla mia risposta che funziona. –

-1

Dipende da quale sia la tua chiave - la natura di una hashmap non consente che ciò accada facilmente.

Il modo in cui riesco a pensare in cima alla mia testa è selezionare un numero casuale compreso tra 1 e la dimensione dell'hashmap, e quindi iniziare a scorrere su di esso, mantenendo un conteggio mentre si va - quando il conteggio è uguale a quel numero casuale che hai scelto, questo è il tuo elemento casuale.

+0

Kazer La chiave e il valore I nella mia mappa sono entrambi di tipo String, è in questa situazione che ho affrontato questo problema. C'è un metodo value() che restituisce una vista dell'insieme in base alla documentazione, è possibile attraverso questo per prendere solo i valori di HashMap (non chiavi) e ottenere un valore casuale? – Varuna

+0

Naturalmente, sebbene ciò sollevi la domanda sul perché stai usando una mappa di hash in primo luogo ... –

3

Genera un numero casuale compreso tra 0 e il numero di tasti nel numero HashMap. Ottieni la chiave al numero casuale. Ottieni il valore da quella chiave.

Pseudocodice:

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

Se è difficile da attuare questo in Java, quindi è possibile creare e matrice da questo codice utilizzando la funzione toArray() in Set.

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

Non sono proprio sicuro di come eseguire il numero casuale.

+2

tranne che è intraducibile in Java, poiché le chiavi di una Mappa sono un Set, e gli Insiemi non hanno alcun senso della posizione – kdgregory

+1

Penso che la mia seconda idea funzionerà meglio. –

+0

Rimuovere il downvote perché hai cercato i metodi corretti (anche se continuo a pensare che l'OP debba ripensare al problema). Dai un'occhiata a java.lang.Random per il numero casuale. – kdgregory

27

Poiché i requisiti chiede solo per un valore casuale dal HashMap, qui è l'approccio:

  1. Il HashMap ha un metodo values che restituisce un valore Collection dei valori nella mappa.
  2. Il Collection viene utilizzato per creare un List.
  3. Il metodo size viene utilizzato per trovare la dimensione dello List, che viene utilizzato dal metodo Random.nextInt per ottenere un indice casuale di List.
  4. Infine, il valore viene recuperato dal metodo Listget con l'indice casuale.

Implementazione:

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

La parte bella di questo approccio è che tutti i metodi sono generic - non v'è alcuna necessità di fusione di caratteri.

+1

Gentile coobird il tuo codice è corretto è e funziona Ma io scelgo quanto sopra da Tom dato che mi sembra come più semplice!!!!! Grazie per il vostro aiuto – Varuna

0

Ecco un esempio di come utilizzare gli array approccio descritto da Peter Stuifzand, anche attraverso la values() -Metodo:

// 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

Questo restituisce una chiave e un valore casuali, ma non una coppia che è una mappatura dei valori-chiave nella mappa! –

+0

Sì, ma l'esempio era di mostrare come randomizzare chiavi e valori (vedi commenti OP dal post sopra). :) Tuttavia, chiarirò l'esempio. – MH114

+0

("post above", che significa Kazar, l'ordine è cambiato ..;) – MH114

0

io veramente non so perché si vuole fare questo ... ma se aiuta, ho creato un RandomMap che casualmente automaticamente i valori quando si chiama valori(), allora la seguente applicazione demo eseguibile potrebbe fare il lavoro ...

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

se avete bisogno di disegnare i valori futher dalla mappa senza ripetere alcun elemento puoi mettere il ma p in una lista e quindi mischiarla.

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

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

+1 per Collections.shuffle(); Non l'avevo mai visto prima. Freddo; Grazie! –

1

Una buona risposta dipende un po 'dalle circostanze, in particolare quanto spesso è necessario per ottenere una chiave casuale per una determinata mappa (N.B. la tecnica è essenzialmente lo stesso se si prende chiave o del valore).

  • Se avete bisogno di varie chiavi casuali da una determinata mappa, senza la mappa cambiando tra ottenere i tasti a caso , quindi utilizzare il random sampling method come eseguire iterazioni attraverso il set di chiavi. Effettivamente ciò che fai è scorrere i set restituito da keySet(), e su ogni oggetto calcolare la probabilità di voler prendere quella chiave, dato come quanti hai bisogno complessiva e il numero che hai preso così lontano. Quindi lo genera un numero casuale e verifica se il numero è inferiore alla probabilità . (N.B. Questo metodo funziona sempre, anche se hai solo bisogno 1 chiave, è solo non è necessariamente il modo più efficace in questo caso.)
  • le chiavi in ​​un HashMap sono effettivamente in modo pseudo-casuale già. In un caso estremo dove si solo mai bisogno casuale chiave per una determinata mappa possibile , si potrebbe anche solo estrarre il primo elemento della keySet().
  • In altri casi (in cui si sia bisogno di più chiavi casuali possibili per una determinata mappa possibile, o la mappa cambierà tra di voi prendendo casuali chiavi), si deve essenzialmente alla creare o mantenere una matrice/lista delle chiavi da cui si seleziona una chiave casuale .
+1

... tranne per il fatto che l'OP voleva valori casuali, non chiavi – kdgregory

+0

ah, mi dispiace non averlo notato - lo stesso si applica essenzialmente, anche se si prende la chiave o il valore. –

-1

Questo è un esercizio dal libro Objects First With Java.It è il seguente.PickDefaultResponse() genera una risposta usando un ArrayList e ottenendo un numero intero generato a caso e utilizzandolo come indice di ArrayList. Questo funziona Ma quello che volevo superare era il riempimento di due elenchi. Qui l'ResponseMap dell'istanza di HashMap e l'istanza di ArrayList defaultResponses devono essere compilati separatamente.

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

Torna indietro e pensa davvero al motivo per cui questi due oggetti esistono. Effettuare effettivamente il programma e vedere come si comporta quando si digitano valori diversi. Perché questi due oggetti hanno scopi molto diversi. – kdgregory

+0

E in futuro, ti preghiamo di modificare il tuo post originale. – kdgregory

+0

si si quello che ho fatto è sbagliato, non è venuto da me modificare l'originale in quel momento, mi dispiace per quello !! La responseMap (HashMap) fornisce all'utente risposte in base al proprio input.The defaultResponses (ArrayList) genera una risposta utilizzando un indice casuale, se responseMap (HashMap) non fornisce all'utente una risposta.Il comando da parte dell'utente l'utente è la chiave di HashMap, se è già mappato con una delle chiavi in ​​HashMap, viene emessa una risposta altrimenti viene chiamato pickDefaultResponses() e viene utilizzato un indice generato casualmente per ottenere un valore da ArrayList. Varuna Varuna – Varuna

0

Di solito non si vuole davvero un valore casuale , ma piuttosto appena qualsiasi valore, quindi è bello fare questo:

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

convertendolo in un array e quindi ottenere il valore è troppo lento quando è nel percorso caldo.

in modo da ottenere il set (sia il set di chiavi o keyvalue) e fare qualcosa di simile:

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

Ho scritto un programma di utilità per recuperare una voce casuale, chiave, o un valore da una mappa, insieme ingresso, o iteratore.

Poiché non è possibile e non si deve essere in grado di calcolare la dimensione di un iteratore (Guava can do this), sarà necessario sovraccaricare il metodo randEntry() per accettare una dimensione che dovrebbe corrispondere alla lunghezza delle voci.

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