2012-03-29 21 views
27

Desidero ottenere chiavi casuali e i rispettivi valori da una mappa. L'idea è che un generatore casuale prenda una chiave e visualizzi quel valore. La parte difficile è che sia la chiave che il valore saranno stringhe, ad esempio myMap.put("Geddy", "Lee").Selezione di chiavi e set di valori casuali da una mappa in Java

+0

Quali sono i tuoi criteri di prestazione? C'è una soluzione O (n) per scegliere un elemento casuale da una sequenza arbitraria, ma se sceglierai elementi casuali un * lotto * dalla stessa mappa, potresti voler creare un elenco delle chiavi in ​​modo da poter basta scegliere un numero casuale e passare da quello. –

+0

Ho due modi che immagino: una chiave viene chiamata in modo casuale e visualizzata, l'utente inserisce un valore, il valore inserito viene confrontato con il valore memorizzato con la chiave. Dal mio esempio precedente, diciamo che all'utente viene chiesto "Qual è il cognome di Geddy?" (dove "Geddy" sarebbe preso come una stringa dalla chiave), l'utente inserisce "Smith". "Smith" viene quindi controllato rispetto al valore "Lee", ecc ... – Roberto

risposta

44
HashMap<String, String> x; 

Random  random = new Random(); 
List<String> keys  = new ArrayList<String>(x.keySet()); 
String  randomKey = keys.get(random.nextInt(keys.size())); 
String  value  = x.get(randomKey); 
+5

Vorrei inserire entrySet() nella lista. –

+0

Grazie! E 'stato davvero d'aiuto. – Roberto

+0

Devo aggiungere che non ho rivelato che dovevo implementarlo in Android. Alla fine ho finito con l'utilizzo di SharedPreferences -> Map and Map -> ArrayList. – Roberto

3

Se non ti interessa lo spazio sprecato, un approccio sarebbe quello di mantenere separatamente un List di tutte le chiavi che si trovano nello Map. Per prestazioni ottimali, è necessario un List con buone prestazioni ad accesso casuale (come un ArrayList). Quindi, ottieni un numero casuale compreso tra 0 (incluso) e list.size() (esclusivo), estrai la chiave in quell'indice e guarda quella chiave.

Random rand = something 
int randIndex = rand.nextInt(list.size()); 
K key = list.get(randIndex); 
V value = map.get(key); 

Questo approccio significa anche che l'aggiunta di una coppia valore-chiave è molto più economica rispetto alla rimozione di una coppia chiave-valore. Per aggiungere la coppia valore-chiave, devi verificare se la chiave è già presente nella mappa (se i tuoi valori possono essere nulli, devi chiamare separatamente map.containsKey; in caso contrario, puoi semplicemente aggiungere la coppia chiave-valore e vedere se il "vecchio valore" restituito è null). Se la chiave è già nella mappa, l'elenco è invariato, ma in caso contrario, si aggiunge la chiave alla lista (un'operazione O (1) per la maggior parte degli elenchi). La rimozione di una coppia valore-chiave, tuttavia, implica un'operazione O (N) per rimuovere la chiave dall'elenco

Se lo spazio è un grosso problema, ma le prestazioni lo sono meno, potresti anche ottenere un Iterator sulla mappa set di voci (Map.entrySet()) e salta le voci randIndex prima di restituire quello desiderato.Ma sarebbe un'operazione di tipo O (N), che blocca l'intero punto di una mappa

Infine, è possibile ottenere il set di voci toArray() e indicizzarlo a caso. È più semplice, anche se meno efficiente.

+0

Questo è stato davvero utile. – Roberto

-1

Da un po 'di tempo da quando si gioca con java, ma non keySet() si fornisce un elenco che è possibile selezionare utilizzando un indice numerico? Penso che potresti scegliere un numero casuale e selezionarlo dal tasto Set di myMap, quindi selezionare il valore corrispondente da myMap. Non posso testarlo adesso, ma sembra che mi colpisca il più possibile!

+0

No, come suggerisce il nome che restituisce un 'Set' e non un' Elenco'. Quindi sì, puoi scegliere una chiave a caso da un 'Set', ma non così semplice (né altrettanto veloce) come da un' Elenco' – Robin

1

Vorrei copiare la mappa in una matrice e selezionare la voce desiderata a caso. Questo evita la necessità di cercare anche il valore dalla chiave.

Map<String, String> x = new HashMap<String, String>(); 
Map.Entry<String,String>[] entries = x.entrySet().toArray(new Map.Entry[0]); 
Random rand = new Random(); 

// call repeatedly 
Map.Entry<String, String> keyValue = entries[rand.nextInt(entries.length)]; 

Se si vuole evitare la duplicazione, è possibile casuale l'ordine delle voci

Map<String, String> x = new HashMap<String, String>(); 
List<Map.Entry<String,String>> entries = new ArrayList<Map.Entry<String, String>> (x.entrySet()); 
Collections.shuffle(entries); 
for (Map.Entry<String, String> entry : entries) { 
    System.out.println(entry); 
} 
1

Utilizzare reservoir sampling per selezionare un elenco di chiavi casuali, poi inserirli in una mappa (insieme con i corrispondenti valori nella mappa sorgente)

In questo modo non è necessario copiare l'intero keySet in un array, ma solo i tasti selezionati.

public static <K, V>Map<K, V> sampleFromMap(Map<? extends K, ? extends V> source, int n, Random rnd) { 
    List<K> chosenKeys = new ArrayList<K>(); 
    int count = 0; 
    for (K k: source.keySet()) { 
     if (count++ < n) { 
      chosenKeys.add(k); 
      if (count == n) { 
       Collections.shuffle(chosenKeys, rnd); 
      } 
     } else { 
      int pos = rnd.nextInt(count); 
      if (pos < n) { 
       chosenKeys.set(pos, k); 
      } 
     } 
    } 
    Map<K, V> result = new HashMap<K, V>(); 
    for (K k: chosenKeys) { 
     result.put(k, source.get(k)); 
    } 
    return Collections.unmodifiableMap(result); 
} 
+0

. Cercherò di approfondirlo ulteriormente. – Roberto

2

se le chiavi sono integer o qualcosa di simile, è possibile utilizzare TreeMap per farlo.

TreeMap<Integer, Integer> treeMap = new TreeMap<>(); 
int key = RandomUtils.ranInt(treeMap.lastKey()); 
int value = treeMap.ceilingKey(key); 
0
In some cases you might want to preserve an order you put the elements in the Set, 
In such scenario you can use, This 

Set<Integer> alldocsId = new HashSet<>(); 
      for (int i=0;i<normalized.length;i++) 
      { 
       String sql = "SELECT DISTINCT movieID FROM postingtbl WHERE term=?"; 
       PreparedStatement prepstm = conn.prepareStatement(sql); 
       prepstm.setString(1,normalized[i]); 
       ResultSet rs = prepstm.executeQuery(); 
       while (rs.next()) 
       { 
        alldocsId.add(rs.getInt("MovieID")); 
       } 
       prepstm.close(); 
      } 

     List<Integer> alldocIDlst = new ArrayList<>(); 
     Iterator it = alldocsId.iterator(); 
     while (it.hasNext()) 
     { 
      alldocIDlst.add(Integer.valueOf(it.next().toString())); 
     } 
Problemi correlati