【问题标题】:Java comparator: Two ordering criteriaJava 比较器:两个排序标准
【发布时间】:2013-06-06 09:57:04
【问题描述】:

我有一个简单的类,它包含一个字符串(姓名)和一个整数(年龄)。应存储在集合中的对象不得具有双重名称值,并且应按年龄降序排序。 第一个代码示例删除了所有双名,但不包含第二个排序标准:

public int compare(Person p1, Person p2) {  
    int reVal = 1;

       if(p1.getName().compareTo(p2.getName()) != 0){
       reVal = 1;       
       }
       else {
       reVal = 0;       
       }                               
         return reVal;                  
    } 

下一个示例比较器将对不包含任何双重名称的其余对象集进行排序:

public int compare(Person p1, Person p2) {  
    boolean ageGt = (p1.getAge() > p2.getAge());
    int reVal = 1;

       if(p1.getName().compareTo(p2.getName()) != 0){
       if(scoreGt)
            reVal = -1;
       else 
            reVal = 1;      
       }
       else {
       reVal = 0;       
       }                               
         return reVal;                  
    } 

第二个比较器根据对象的年龄值正确地对对象进行排序,但它允许使用双重名称,我不明白,因为外部 if 语句已经检查了两个对象的名称是否相等。为什么会这样?

【问题讨论】:

  • 双名是什么意思?
  • @kocko 我认为他的意思是他不能有两个具有相同名称的对象,因此如果它们确实具有相同的名称,则它们被认为是相等的。
  • Comparator 不是用于测试唯一性。如果你想测试名字的唯一性,你应该使用Map,名字作为键,人作为值。
  • compare 用于根据某种排序比较对象。不涉及过滤,因此已排序(有序)集合与未排序集合具有相同数量的对象。如果您需要唯一性,就您的名称而言,请尝试使用专用的 .hashCode() 方法将对象放入一个集合中。
  • 我使用了一个集合,并且第一段代码按预期工作,并且不允许添加第二个元素,该元素的名称已包含一个元素。但是,第二段代码不起作用。我想我忘了实现 hashCode() 方法。

标签: java collections comparator


【解决方案1】:

你有一个基本问题:你想同时测试唯一性来排序条目。没有内置集合会同时检查条目是否相等并且它们的比较是否为 0。

例如,两个Set 实现是HashSetTreeSet

  • HashSet 使用Object.equals()/.hashCode() 来测试是否相等;
  • TreeSet 使用 Comparator(或对象的 Comparable 功能,如果它们实现了它)来测试相等性。

这不是一回事。事实上,对于一个特定的 JDK 类,即BigDecimal,这可能会让人非常惊讶:

final BigDecimal one = new BigDecimal("1");
final BigDecimal oneDotZero = new BigDecimal("1.0");

final Set<BigDecimal> hashSet = new HashSet<>();
// BigDecimal implements Comparable of itself, so we can use that
final Set<BigDecimal> treeSet = new TreeSet<>();

hashSet.add(one);
hashSet.add(oneDotZero);
// hashSet's size is 2: one.equals(oneDotZero) == false

treeSet.add(one);
treeSet.add(oneDotZero);
// treeSet's size is... 1! one.compareTo(oneDotZero) == 0

你不能一边吃蛋糕一边吃。在这里,你要根据名字测试唯一性,根据年龄比较,你必须使用Map

要获得排序的人员列表,您必须复制此地图的.values() 作为列表并使用Collections.sort()。如果你使用 Guava,后半部分就像 Ordering.natural().sortedCopy(theMap.values()) 一样简单,前提是你的值实现了 Comparable

【讨论】:

  • 谢谢。覆盖对象类的equals() 方法也是一个不错的选择吗?在这种情况下,我只需要在将对象 o1 添加到集合之前调用 mySet.contains(o1)。之后,根据equals-contract保证唯一性。
  • 如果您覆盖.equals(),您还必须覆盖.hashCode()!是的,如果您在取决于相等性的 Set 中使用这些值,那么无论如何您都必须这样做。
  • hashCode() 仅对基于哈希的集合是必需的,不是吗?其他人只是忽略它。在这种情况下,name.hashCode() 可以是一个简单但可行的哈希码吗?谢谢。
  • hashCode() 在您覆盖 equals() 时是必需的;这是Object 的基本合约。不要破坏它!
猜你喜欢
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多