【问题标题】:Sorting by multiple keys in nested HashMap按嵌套 HashMap 中的多个键排序
【发布时间】:2014-02-24 00:32:58
【问题描述】:

我目前有一个设置,其中数据结构是一个 ArrayList,每个键都包含一个 HashMap,用于 ArrayList 中的每个键。我想要做的是能够通过 HashMap 本身的一个或多个键进行排序。在我的研究中,大多数建议似乎是使用 Collections.sort(ArrayList,comparatorFunction()) 然后构建一个自定义 Comparatorfunction 来进行排序,但对于如何构建一个 Comparator 来说完全是一个菜鸟......我不知道甚至不知道从哪里开始,更不用说构建一个'我敢肯定这不是一个简单的设置。有谁碰巧知道一些对解决此类功能有用的资源?

编辑:抱歉,一些示例结构会有所帮助。

如果您调用 arrayList.get(0) 并对其执行 System.out.println,它会返回说 {town=Toronto, population=2,500,000, age=147},我想要做的是所以我可以说按人口排序 ArrayList,然后是年龄。

【问题讨论】:

  • "一个 ArrayList,每个键都包含 ArrayList 中每个键的 HashMap" 一个 ArrayList 没有键,所以...你是什么意思?你能举个例子说明这些是如何声明的以及你想按什么排序吗?

标签: java


【解决方案1】:

通常在这种情况下,比较器的工作是简单地从其他东西返回比较值。例如,这里有一个 Comparator,它将按字母顺序排列 Fonts:

class FontAlphabetizer
implements Comparator<Font> {
    @Override
    public int compare(Font font1, Font font2) {
        return font1.getName().compareTo(font2.getName());
    }
}

这其实很简单:getName 返回一个字符串,我们所做的就是返回 String 的 compareTo 方法的值。

在这里,您似乎拥有ArrayList&lt;Map&gt;,并且您希望根据从地图中选择的值对 ArrayList 进行排序。所以你需要的是Comparator&lt;Map&gt;。您需要为 Comparator 提供您想要排序的相应值的键。这可以概括如下:

class MapValueComparator<K, V extends Comparable<V>>
implements Comparator<Map<K, V>> {
    final K key;

    MapValueComparator(K key) {
        this.key = key;
    }

    @Override
    public int compare(Map<K, V> map1, Map<K, V> map2) {
        return map1.get(key).compareTo(map2.get(key));
    }
}

这是一个比较 Map 的 Comparator,它在声明中指定 Map 的值也必须是 Comparable。它根据从给定键中检索到的值进行比较。

例如,如果我们有一个ArrayList&lt;Map&lt;String, String&gt;&gt;,我们可以按"town" 中的值排序,如下所示:

static void sortByTown(List<Map<String, String>> list) {
    Collections.sort(list, new MapValueComparator<String, String>("town"));
}

打嗝是你说你有town=Toronto, population=2,500,000,这表明你想要排序的人口是一个字符串(因为它可能与多伦多在同一张地图上)。可能不需要将人口与字符串进行比较,因为它会按字典顺序排序(50 在 2,500,000 之后,因为 5 在 2 之后)。在这种情况下,通用版本可能无法工作,因为您需要采取额外的步骤将值转换为数字。

class PopulationComparator
implements Comparator<Map<String, String>> {
    @Override
    public int compare(Map<String, String> map1, Map<String, String> map2) {
        final Long pop1 = Long.valueOf(map1.get("population"));
        final Long pop2 = Long.valueOf(map2.get("population"));

        return pop1.compareTo(pop2);
   }
}

(附带说明,如果您的人口包含逗号,您需要在将其解析为数字之前对其进行格式化。您可以使用 replaceAll("\\D", "") 从字符串中删除所有非数字。)

在这种情况下,为此创建一个类而不是使用 Map 可能是有利的。然后你可以让数字字段是数字类型。如果你有一个类,那么比较基本上是一样的:只是返回一个选定字段的比较。

【讨论】:

    【解决方案2】:

    自定义比较器可用于定义可以比较类的对象的方式。它具有以下语法:

     public class CustomComparator implements Comparator<MyObjectType>
     {
      public int compare(MyObjectType ob1 , MyObjectType ob2)
      {
         //code to compare the 2 objects
      }
     }
    

    有关创建 Comparator 类以对集合中的元素进行自定义排序的信息,请参阅以下链接:link

    【讨论】:

      【解决方案3】:

      这是您要查找的内容:

      final List<Map<String, Object>> towns = new ArrayList<Map<String, Object>>();
      
      final Map<String, Object> toronto = new HashMap<String, Object>();
      toronto.put("town", "Toronto");
      toronto.put("population", 2500000);
      toronto.put("age", 147);
      towns.add(toronto);
      
      final Map<String, Object> ottawa = new HashMap<String, Object>();
      ottawa.put("town", "Ottawa");
      ottawa.put("population", 883000);
      ottawa.put("age", 159);
      towns.add(ottawa);
      
      final Map<String, Object> montreal = new HashMap<String, Object>();
      montreal.put("town", "Montreal");
      montreal.put("population", 1600000);
      montreal.put("age", 372);
      towns.add(montreal);
      
      final Map<String, Object> quebec = new HashMap<String, Object>();
      quebec.put("town", "Quebec City");
      quebec.put("population", 600000);
      quebec.put("age", 406);
      towns.add(quebec);
      
      final Map<String, Object> vancouver = new HashMap<String, Object>();
      vancouver.put("town", "Vancouver");
      vancouver.put("population", 600000);
      vancouver.put("age", 128);
      towns.add(vancouver);
      
      Collections.sort(towns, new Comparator<Map<String, Object>>() {
          @Override
          public int compare(final Map<String, Object> o1, final Map<String, Object> o2) {
              if (o1.get("population") instanceof Integer && o2.get("population") instanceof Integer && !((Integer)o1.get("population")).equals((Integer)o2.get("population"))) {
                  return ((Integer)o1.get("population")).compareTo((Integer)o2.get("population"));
              }
              if (o1.get("age") instanceof Integer && o2.get("age") instanceof Integer) {
                  return ((Integer)o1.get("age")).compareTo((Integer)o2.get("age"));
              }
              // Default if there is no population/no age, shouldn't happen.
              // TODO : do something else.
              return o1.toString().compareTo(o2.toString());
          }
      });
      
      for (final Map<String, Object> town: towns) {
          System.out.println(town.get("population")+"\t"+town.get("age")+"\t"+town.get("town"));
      }
      

      代码的第一部分只是根据您所说的创建ArrayList,然后我们使用自定义ComparatorList进行排序,并打印结果。

      这是输出:

      600000  128 Vancouver
      600000  406 Quebec City
      883000  159 Ottawa
      1600000 372 Montreal
      2500000 147 Toronto
      

      如您所见,它先按人口排序,然后按年龄排序。

      但是,也许最好的解决方案是创建一个对象Town,包含三个字段(namepopulationage),并使用此对象而不是HashMaps。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-16
        • 2023-03-17
        • 2017-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多