【问题标题】:Compare HashMap with Objects将 HashMap 与对象进行比较
【发布时间】:2012-01-21 23:46:53
【问题描述】:

我有一个hashmap<CustomObject,Integer>,我想比较每个条目中的整数(值)。所以,基本上我想按它们的Integer 值按降序对我的值进行排序。我有一个Comparator,其中包含以下内容...

class Compare implements Comparator<Integer>{
    Map<CustomObject,Integer> map;
    /**
     * Constructs our map
     * @param map map to be sorted.
     */
    public Compare(Map<CustomObject,Integer> map){
        this.map = map;
    }
    /**
     * Performs the comparison between two entries.
     */
    public int compare(Integer one, Integer two){
        if(map.get(one) <= map.get(two)){
            return 1;
        }else{
            return 0;
        }
    }
}

我通过调用以下代码行将我的Hashmap 传递到 TreeMap..Tmap.putAll(Hmap);。其中Tmap和Hmap定义为:

private HashMap<CustomObject,Integer> Hmap;
private TreeMap<CustomObject,Integer> Tmap;

当我运行我的代码时,我收到错误Exception in thread "main" java.lang.ClassCastException: CustomObject cannot be cast to java.lang.Comparable

当我尝试从排序列表中提取值时,似乎调用了异常。就这样……

TreeMap<CustomObject,Integer> sorted = Tmap.putAll(hmap);
sorted.get(o);

其中o 是一个自定义对象。

我想我误解了比较器的工作原理。我做错了什么?我将如何比较这两个 Integer 值?

编辑

只是为了澄清我实际上想要做什么......

我想比较链接到 CustomObject 的整数。我无法将密钥设为整数,因为这些整数可能不是唯一的。我要比较它们,因为我想根据它们的 Integer 值按降序对我的集合进行排序。

【问题讨论】:

  • 抛出异常的代码在哪里??
  • 问题是地图的比较器比较的是键,而不是值。除了做一个反向映射,然后检查它,然后将结果插入 LinkedHashMap 之外,我没有看到任何解决您的问题的方法。不是你想要的,我希望......
  • @SJuan76 我已经更新了我的代码以反映这一点
  • @fge 您建议根据键值对中的整数值对集合进行排序的最佳方法是什么?
  • 好吧,我刚刚发现了这个:*.com/questions/109383/…

标签: java hashmap


【解决方案1】:

您需要更改比较器以比较 CustomObjects,而不是 Integers

class Compare implements Comparator<CustomObject>{
    Map<CustomObject,Integer> map;
    /**
     * Constructs our map
     * @param map map to be sorted.
     */
    public Compare(Map<CustomObject,Integer> map){
        this.map = map;
    }
    /**
     * Performs the comparison between two entries.
     */
    public int compare(CustomObject left, CustomObject right){
        return map.get(left).compareTo(map.get(right));
    }
}

然后,您需要告诉TreeMap 使用您的比较器:

private Map<CustomObject,Integer> Tmap = 
    new TreeMap<CustomObject,Integer>(new Compare(HMap));

【讨论】:

  • 但是.. 我需要比较整数值而不是它们的键(CustomObjects)
  • @Skizit:这就是你在比较器中调用map.get 的原因。这将检索与CustomObject 对应的整数值。结果是CustomObjects 按其对应的整数值排序。
【解决方案2】:
new TreeMap<..>(new Compare<..>(map))

您必须在构建树时指定比较器。否则,它会假定您的密钥是可比较的(但它们不是)

但是check this answer 用于根据值对地图进行排序。

【讨论】:

    【解决方案3】:

    这种方法存在一些问题。

    • TreeMap 忽略重复项(当 compareTo 返回 0 时)在您的情况下,它只会添加递减的整数值。您可以修复它,使数字可以按任何顺序排列,但它仍会删除任何具有重复值的条目。
    • compareTo 中使用的字段不能更改,否则会损坏集合。
    • 您需要知道整个键才能查找值。在这种情况下,您需要知道这种情况下的值才能进行查找,这可能不是很有用。

    您最好从您可以排序的map.entrySet() 创建一个List&lt;Entry&lt;CustomObject,Integer&gt;&gt;,因为这允许重复并且是有序的。

    【讨论】:

      【解决方案4】:

      我认为问题在于您使用了错误的TreeMap 构造函数。您使用的要求所有键都是实现Comparable 的类的实例。你的CustomObject 没有。您应该使用带有Comparator 参数的构造函数;例如

      TreeMap<CustomObject,Integer> tmap = 
          new TreeMap<CustomObject,Integer>(new Compare());
      

      这还会告诉您,您的 Compare 类需要实现 Comparator&lt;CustomObject&gt; 而不是 Comparator&lt;Integer&gt;

      另一个问题是您的比较器没有实现正确的语义。如果 arg1 arg2 则返回 +ve 数字;例如

      public int compare(CustomObject one, CustomObject two){
          return Integer.compare(map.get(one), map.get(two));
      }
      

      甚至这是狡猾的:

      • 如果映射到同一整数的任意两个 CustomObject 实例将被视为相等,您将无法在 TreeMap 中同时拥有两个(不同的)键。

        李>
      • 如果在 map 中没有针对 onetwo 的条目,您的比较器将抛出 NPE。

      【讨论】:

        【解决方案5】:

        我建议使用整数索引的多图。如果您需要保留按对象查找这些对的能力,您可以维护这两个映射。 Java 不附带多图,但有一些简单的解决方案。 HereMap&lt;Integer,List&lt;Object&gt;&gt; 的一个示例(向下滚动到多地图部分)。

        【讨论】:

          【解决方案6】:

          首先,key 应该是 Integer,value 应该是 CustomObject,然后可以根据比较器对 HashMap 进行排序。但是默认情况下 HashMap 或者如果你想对 CustomObject 进行排序,那么你必须使 CustomObject 实现 Comparable 并编写比较方法,这将使 HashMap 基于 CustomObject 进行排序。如果你明白了这一点。并想尝试一下自己的尝试。或者如果你想让我用一个例子来解释我可以做到。

          这个问题令人困惑。

          【讨论】: