web-dev-qa-db-ja.com

Javaのマップからランダムなキーと値のセットを選択する

マップからランダムなキーとそれぞれの値を取得したい。ランダムジェネレーターがキーを選択し、その値を表示するという考え方です。トリッキーな部分は、キーと値の両方が文字列になることです(たとえば、myMap.put("Geddy", "Lee"))。

32
Roberto
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);
50
synopia

この質問はあなたの助けになるはずです JavaでHashMapの値をランダムに取得する方法はありますか? そしてこれも セットからランダムな要素を選ぶ なぜならHashMapHashSetによってサポートされています。それはO(n)時間と一定の空間、またはO(n)余分な空間と一定の時間のいずれかです。

4
Narendra Yadala

無駄なスペースを気にしない場合、1つのアプローチは、ListにあるすべてのキーのMapを個別に保持することです。最高のパフォーマンスを得るには、ランダムアクセスのパフォーマンスが優れているListArrayListなど)が必要です。次に、0(包括的)からlist.size()(排他的)の間の乱数を取得し、そのインデックスでキーを引き出し、そのキーを調べます。

_Random Rand = something
int randIndex = Rand.nextInt(list.size());
K key = list.get(randIndex);
V value = map.get(key);
_

また、このアプローチは、キーと値のペアを追加する方が、削除するよりも安価であることを意味します。キーと値のペアを追加するには、キーが既にマップ内にあるかどうかをテストします(値がnullになる可能性がある場合は、_map.containsKey_を個別に呼び出す必要があります。そうでない場合は、キーと値のペアで、返される「古い値」が_null)_かどうかを確認します。キーが既にマップにある場合、リストは変更されませんが、そうでない場合は、リストにキーを追加します(O(1)ほとんどのリストに対する操作)。ただし、キーと値のペアの削除には、リストからキーを削除するためのO(N)操作が含まれます。

スペースが大きな懸念事項であるが、パフォーマンスがそれほど高くない場合、マップのエントリセット(Map.entrySet())でIteratorを取得し、randIndexエントリをスキップしてから戻ることもできます。あなたが欲しいもの。しかし、それはO(N)操作であり、マップのすべてのポイントを無効にします。

最後に、エントリセットのtoArray()を取得し、それにランダムにインデックスを付けることができます。それは効率的ではありませんが、より簡単です。

3
yshavit

キーが整数または同等のキーである場合、TreeMapを使用してそれを行うことができます。

TreeMap<Integer, Integer> treeMap = new TreeMap<>();
int key = RandomUtils.ranInt(treeMap.lastKey());
int value = treeMap.ceilingKey(key);
2
archeryue

リザーバサンプリング を使用して、ランダムキーのリストを選択し、それらをマップに挿入します(ソースマップの対応する値とともに)。

この方法では、keySet全体を配列にコピーする必要はなく、選択したキーのみをコピーする必要があります。

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);
}
1
finnw

マップを配列にコピーし、必要なエントリをランダムに選択します。これにより、キーから値をルックアップする必要もなくなります。

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

重複を避けたい場合は、エントリの順序をランダム化できます

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
Peter Lawrey
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()));
        }
0
Jagesh Maharjan