【问题标题】:Sorting custom data structure on Key in TreeMap对 TreeMap 中的 Key 上的自定义数据结构进行排序
【发布时间】:2011-09-12 08:35:28
【问题描述】:

我正在尝试按键对 TreeMap 进行排序。关键是一些具有 int、List、String 等的自定义 DataStructure。 我期待排序的成员有一些重复项。假设该成员是Rank。超过 1 个对象可以具有相同的排名。

简化版示例:

注意:在 CompareTo 方法中,不会有意返回 0 以不忽略重复项。(如果这不是避免重复项的正确方法,请纠正我)

import java.util.TreeMap;


public class TreeTest {

public static void main(String[] args) {
    TreeMap<Custom,String> t = new TreeMap<Custom,String>();
    Custom c1 = new Custom();
    c1.setName("a");
    c1.setRank(0);

    Custom c2 = new Custom();
    c2.setName("b");
    c2.setRank(1);

    Custom c3 = new Custom();
    c3.setName("c");
    c3.setRank(0);

    t.put(c1, "first");
    t.put(c2, "Second");
    t.put(c3, "Third");

    System.out.println(t.keySet());

    for(Custom c:t.keySet()){
        System.out.println(t.get(c));
    }
  }
}

和自定义对象

package com.example.ui;

 public class Custom implements Comparable<Custom>{

int rank;
String name;

public int getRank() {
    return rank;
}

public void setRank(int rank) {
    this.rank = rank;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}



@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result + rank;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Custom other = (Custom) obj;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    if (rank != other.rank)
        return false;
    return true;
}

    // 0 is not returned intentionally to NOT ignore duplicates.
 public int compareTo(Custom o) {
    if(o.rank>this.rank)
        return 1;
    if(o.rank==this.rank)
        return -1;
    return -1;
 }
 }

输出::

[com.example.ui.Custom@fa0, com.example.ui.Custom@fbe, com.example.ui.Custom@f80] 无效的 无效的 无效的

预期: 一、二、三分别基于 Rank 0,1,0。

我在 Google 上查看了几个示例。其中大多数是 TreeMap 排序的基本用法,使用具有原始数据类型的键或值,但在排序成员时没有重复 是自定义键 DataStructure 的一部分。

请帮忙?

【问题讨论】:

  • 首先,你的打印声明应该是System.out.println(c.getName());

标签: java sorting treemap


【解决方案1】:

问题是您对compareTo 的实现与TreeMap 所要求的equals 不一致。来自 API 文档:

请注意,排序映射维护的顺序(无论是否 提供了显式比较器)必须与 equals 一致,如果 这个排序后的地图是为了正确实现Map接口。

一种可能的一致实现是首先按排名进行比较,然后在排名值相等时按名称进行比较。对于具有相同等级和相同名称的两个 Custom 实例,您不应该期望能够将它们作为键存储在同一个 Map 中 - 这违反了 Map 的约定

public int compareTo(Custom o) {
  int ret = this.rank - o.rank;

  // Equal rank so fall back to comparing by name.
  if (ret == 0) {
    ret = this.name.compareTo(o.name);
  }

  return ret;
}

【讨论】:

  • +1。另一个问题是,对于具有相同等级的两个对象,您返回 -1,这意味着您处于同时拥有 A &lt; BB &lt; A 的情况,这违反了 Comparable 的约定。
  • 这假设您无法从this.rank - o.rank 获得溢出。这可能是一个安全的假设。即使它是int,也可能不需要。
  • @Peter - 公平点,老实说,我这样做是为了保持示例代码小。
【解决方案2】:

如前所述,您对 equals 和 compareTo 的实现并不一致。如果我正确阅读了您的问题,您需要保留具有相同密钥的重复项。我建议您查看 Google Guava 集合的TreeMultimap。它为每个值对象创建集合容器,以便保留具有相同键的不同值。 例如

treeMultimap.put ("rank1", "Joe");
treeMultimap.put ("rank1", Jane");
treeMultimap.get ("rank1"); // Set("Joe","Jane");

此数据结构的约束是 K,V 对必须是唯一的。也就是说,您不能在 Multimap 中插入 ("rank1", "Joe") 两次。

一个重要提示:您看到这么多使用简单类型(尤其是字符串)的 Map 示例的原因是,映射中的键必须是不可变的。对象的 equals 和 hashcode 值在它用作映射中的键时不得更改。转换为您的示例,您不能执行 customObject.setRank(...) 并在将排名值用作键时更新它。为此,您首先需要删除键及其值,对其进行更新,然后再次插入。

【讨论】:

    【解决方案3】:

    您也可以通过将 Comparator 实现为匿名内部类型并覆盖 compare() 以返回所需的比较来实现。

    public class TreeMaps 
    {
        public static void main(String[] args) 
        {
        Custom c1 = new Custom(1,"A");
        Custom c2 = new Custom(3,"C");
        Custom c3 = new Custom(2,"B");
    
        TreeMap<Custom , Integer > tree = new TreeMap<Custom, Integer>  (new Comparator<Custom>() {
    
                                                @Override
                                                public int compare(Custom o1, Custom o2) {
    
                                                    return o1.rank - o2.rank;
                                                }
                                            });
        tree.put(c1, 1);
        tree.put(c2, 2);
        tree.put(c3, 3);
    
        System.out.println(tree);
    }
    }
    
    class Custom
    {
    int rank ; 
    String name  ; 
    public Custom(int rank , String name) {
        this.rank = rank ;
        this.name = name ;
    
    }
    
    @Override
    public String toString()
    {
        return "Custom[" + this.rank + "-" + this.name + "]" ;
    }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-02-23
      • 1970-01-01
      • 2019-11-22
      • 1970-01-01
      • 2018-01-18
      • 1970-01-01
      • 1970-01-01
      • 2018-06-12
      • 2015-03-08
      相关资源
      最近更新 更多