【问题标题】:Selecting random key and value sets from a Map in Java从 Java 中的 Map 中选择随机键和值集
【发布时间】:2012-04-12 18:24:11
【问题描述】:

我想从 Map 中获取随机键及其各自的值。这个想法是随机生成器将选择一个键并显示该值。棘手的部分是键和值都是字符串,例如myMap.put("Geddy", "Lee")。

【问题讨论】:

  • 您的绩效标准是什么?有一个 O(n) 解决方案可以从任意序列中选择一个随机元素,但是如果您要从同一张地图中选择一个 lot 随机元素,您可能需要创建一个列表键,这样您就可以选择一个随机数并从中获取。
  • 我有两种设想:随机调用一个键并显示,用户输入一个值,输入的值与键存储的值进行检查。在上面的示例中,假设用户被问到“Geddy 的姓氏是什么?” (其中“Geddy”将作为键中的字符串),用户输入“Smith”。然后根据值“Lee”检查“Smith”等...

标签: java dictionary random hashmap treemap


【解决方案1】:
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);

【讨论】:

  • 我会将 entrySet() 放入列表中。
  • 谢谢!这真的很有帮助。
  • 我应该补充一点,我没有透露我必须在 Android 中实现这一点。最终我最终使用了 SharedPreferences -> Map and Map -> ArrayList。
  • 工作就像一个魅力
【解决方案2】:

这个问题应该对你有帮助 Is there a way to get the value of a HashMap randomly in Java? 这个问题也是 Picking a random element from a set 因为HashMap 是由HashSet 支持的。要么是O(n) 时间和恒定空间,要么是O(n) 额外空间和恒定时间。

【讨论】:

    【解决方案3】:

    如果您不介意浪费空间,一种方法是分别保留Map 中所有键的List。为了获得最佳性能,您需要一个具有良好随机访问性能的List(如ArrayList)。然后,只需获取一个介于 0(含)和list.size()(不含)之间的随机数,取出该索引处的键,然后查找该键。

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

    这种方法还意味着添加键值对比删除键值对便宜很多。要添加键值对,您将测试该键是否已在映射中(如果您的值可以为空,则必须单独调用map.containsKey;如果没有,您可以添加键-值对并查看它返回的“旧值”是否为null)。如果键已经在映射中,则列表不变,但如果没有,则将键添加到列表中(O(1)操作大多数列表)。但是,删除键值对涉及从列表中删除键的 O(N) 操作。

    如果空间是一个大问题,但性能不太重要,您还可以在地图的条目集 (Map.entrySet()) 上获得一个 Iterator,并在返回所需的条目之前跳过 randIndex 条目。但这将是一个 O(N) 操作,这有点破坏了地图的全部意义。

    最后,您可以获取条目集的toArray() 并随机索引到它。这更简单,但效率较低。

    【讨论】:

      【解决方案4】:

      如果您的键是整数或类似的,您可以使用 TreeMap 来做到这一点。

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

      【讨论】:

        【解决方案5】:

        我会将地图复制到一个数组中,然后随机选择您想要的条目。这避免了从键中查找值的需要。

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

        【讨论】:

          【解决方案6】:

          使用reservoir sampling 选择随机键列表,然后将它们插入到映射中(连同它们在源映射中的对应值)。

          这样您就不需要将整个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);
          }
          

          【讨论】:

          • 虽然这在理论上是合理的,但keySet() 不是已经分配到一个
          【解决方案7】:
          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()));
                  }
          

          【讨论】:

            【解决方案8】:

            使用 java 已经有一段时间了,但是 keySet() 没有给你一个可以使用数字索引从中选择的列表吗?我认为您可以选择一个随机数并从 myMap 的 keySet 中选择它,然后从 myMap 中选择相应的值。目前无法对此进行测试,但它似乎让我印象深刻!

            【讨论】:

            • 不,顾名思义,它返回 Set 而不是 List。所以是的,您可以从 Set 中选择一个随机密钥,但不如从 List 中直接(也不那么快)
            猜你喜欢
            • 2019-07-30
            • 1970-01-01
            • 2012-09-05
            • 2013-03-28
            • 1970-01-01
            • 2018-06-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多