【问题标题】:Sort HashMap by Value and Convert to New Map按值对 HashMap 进行排序并转换为新 Map
【发布时间】:2019-05-10 20:12:03
【问题描述】:

我正在尝试将Map<String, Map<String, String>> 转换为LinkedHashMap<String, Double>,同时按特定的内部value 进行排序。我的挣扎不在于排序,而在于过程中的转换。

鉴于这些孩子 Map 和 1 个父母 Map

Map<String, String> man = new HashMap<>();
man.put("h_ratio", "0.45");
man.put("w_ratio", "0.75");
man.put("a_ratio", "0.19");

Map<String, String> bear = new HashMap<>();
bear.put("h_ratio", "0.23");
bear.put("w_ratio", "0.72");
bear.put("a_ratio", "0.95");

Map<String, String> pig = new HashMap<>();
pig.put("h_ratio", "0.37");
pig.put("w_ratio", "0.64");
pig.put("a_ratio", "0.81");

Map<String, Map<String, String>> unsortedOrganisms = new HashMap<>();
unsortedOrganisms.put("man", man);
unsortedOrganisms.put("bear", bear);
unsortedOrganisms.put("pig", pig);

我如何有效地创建LinkedHashMap&lt;String, Double&gt;a_ratio 排序,结果是:

{bear={0.95}, pig={0.81}, man={0.19}}

unsortedOrganisms的真实大小为50000,每个内部Map的真实大小为50。

Research shows最高效的方式可能是

从地图中创建Entry 集合中的List,然后使用以下方法对List 进行排序 Collections.sort()

但是,该示例假定输出 Map 与输入映射具有相同的结构。而我的结果需要转换为&lt;String, Boolean&gt; 类型的LinkedHashMap

【问题讨论】:

  • “但是该示例假定输出映射与输入映射具有相同的结构” 该示例不会创建“输出映射”。它创建一个列表,对其进行排序,然后将其保留为列表。
  • 哦,对了。但第一行是Set&lt;Entry&lt;String, Integer&gt;&gt; set = map.entrySet();,然后传递到创建的List。我不知道如何将我的map.entrySet() 有效地转换为我应该创建的List,然后如图所示进行排序。
  • 我的第一行是Set&lt;Map.Entry&lt;String, Map&lt;String, String&gt;&gt;&gt; set = unsortedOrganisms.entrySet(); List&lt;Map.Entry&lt;String, Map&lt;String, String&gt;&gt;&gt; list = new ArrayList&lt;&gt;(set); 所以我正在对格式不正确的列表进行排序。
  • 顺便说一句,这是存储这些数据的一种糟糕的方式:为有机体定义一个具有三个字段的类,每个字段一个;并将比率实际存储为数字类型。

标签: java collections hashmap


【解决方案1】:

您可以使用 Java Streams 来解决这个问题:

LinkedHashMap<String, Double> result = unsortedOrganisms.entrySet().stream()
        .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), Double.valueOf(e.getValue().get("a_ratio"))))
        .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (d1, d2) -> d2, LinkedHashMap::new));

结果会是这样的:

{bear=0.95, pig=0.81, man=0.19}

【讨论】:

  • 是的,就是这样。我现在将研究内部机制。谢谢。一旦我将效率与其他帖子进行比较,可能会接受。
  • 结果出来了,速度差异似乎可以忽略不计,所以我接受了你的答案,因为它是第一个,紧凑且正确。谢谢你。速度演示:pastebin.com/P3QLRX16
【解决方案2】:

使用流最简单:

input.entrySet().stream()
    // Get just the (key, a_ratio) pair
    .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), e.getValue().get("a_ratio"))
    // Sort the entries by value, descending.
    .sorted(Map.Entry.comparingByValue().reversed())
    .collect(toList());

我将它保留为一个列表,因为“按值排序”的映射没有意义:映射是基于键的结构。如果您要向映射添加另一个键/值对,则必须从头开始重建它。

【讨论】:

  • 我明白你的意思,这需要一些返工,但肯定会长期避免技术债务。目前我正在循环排序地图并以各种方式使用键。但是,我肯定应该创建一个具有适当数据类型的对象。
【解决方案3】:

没有流的老式版本:

  public static LinkedHashMap<String, Double> sortOrganisms(Map<String, Map<String, String>> data, String key){

    ArrayList<AbstractMap.SimpleEntry<String, Double> > organisms = new ArrayList<>(data.size());

    for (String name : data.keySet()) {
      Double value = Double.parseDouble(data.get(name).get(key));
      organisms.add(new AbstractMap.SimpleEntry<>(name, value));
    }

    organisms.sort(new Comparator<AbstractMap.SimpleEntry<String, Double> >() {
      @Override
      public int compare(AbstractMap.SimpleEntry<String, Double> o1, AbstractMap.SimpleEntry<String, Double> o2) {
        // reverse sorting by value
        return -Double.compare(o1.getValue(),o2.getValue());
      }
    });

    LinkedHashMap<String, Double> result = new LinkedHashMap<>();
    for (AbstractMap.SimpleEntry<String, Double> o : organisms) {
      result.put(o.getKey(), o.getValue());
    }

    return result;

  }

【讨论】:

  • 您的解决方案有效,并且基本上与其他解决方案一样快。然而,另一张海报是第一位的,并提供了一个紧凑的答案。谢谢你的时间。我也从你的代码中学到了。速度演示:pastebin.com/P3QLRX16
猜你喜欢
  • 1970-01-01
  • 2021-03-12
  • 2018-12-06
  • 2016-01-21
  • 2011-12-28
  • 1970-01-01
  • 2015-07-16
  • 1970-01-01
  • 2011-11-05
相关资源
最近更新 更多