19

据说当输入参数为null时,compareTo()应该抛出NullPointerException。但是,我正在实现一个需要将字段与字符串类型进行比较的类。这些字段不必是强制性的。我想知道在这种情况下,

1)当输入为空时我应该返回什么?任何非空字符串在字典上是否应该大于或小于空?

2)如果这被认为是不好的做法,是否有任何支持论据?我应该强制用户使用空字符串吗?如果使用空字符串,会不会混淆字段不适用和字段为空的情况?如果必须抛出异常,那么除了在手册中警告用户之外,我还能/应该做什么?

编辑:我可能在这里表达得不够清楚,但是在我正在实现的程序中,可能为空的字符串都是字段或类,不应为空。换句话说,comparedTo() 使用的对象不能为空,只有它们的私有字段可以。所以在这种情况下,我相信如果我正确地实现 compareTo(),它不会违反传递性要求,因为具有空字段的类总是被认为是相同的。我是对的还是我解释错了?

谢谢大家的回答!

4

6 回答 6

24

来自 javadocComparable

请注意,null 不是任何类的实例,即使 e.equals(null) 返回 false,e.compareTo(null) 也应该抛出 NullPointerException。

于 2011-06-07T00:02:57.590 回答
15

是的,允许例如字段没有问题null- 只需确保定义了它的排序顺序。最自然的是将它放在所有真实字符串之前或之后,但是您可以在这里做任何事情,只要始终如一地做。(例如,您可以排序null"null"。)

这是单个成员的示例实现:

class Example implements Comparable<Example> {

   @Nullable
   private String member;

   // TODO: getter, setter, constructor, ...

   public int compareTo(Example that) {
      if(this.member == null)
         if(that.member == null)
            return 0; //equal
         else
            return -1; // null is before other strings
       else // this.member != null
         if(that.member == null)
            return 1;  // all other strings are after null
         else
            return this.member.compareTo(that.member);
   }
}

请注意,Comparable.compareTo() 的规范只有一个约束o.compareTo(null)(它的行为应该像- null.compareTo(o),即抛出 NullPointerException),而不是关于如何null处理字段(它根本没有提到字段,所以类可以返回它想要的任何东西,只要保证反对称性、自反性和传递性)。

于 2011-06-07T00:20:54.103 回答
7

不抛出异常是一种不好的做法,因为它违反了compareTo 的传递反对称性质。

Comparable.compareTo文档:

实现者必须确保所有 x 和 y 的 sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。(这意味着如果 y.compareTo(x) 抛出异常,则 x.compareTo(y) 必须抛出异常。)

实现者还必须确保关系是可传递的:(x.compareTo(y)>0 && y.compareTo(z)>0) 意味着 x.compareTo(z)>0。

最后,实现者必须确保对于所有 z,x.compareTo(y)==0 意味着 sgn(x.compareTo(z)) == sgn(y.compareTo(z))。

更重要的是,在对象上使用 compareTo 将它们与字符串进行比较是一个坏主意,原因相同:sign(obj.compareTo(str)) != -sign(str.compareTo(obj)). 实现一个自定义比较器并在其中做任何你想做的事情。

于 2011-06-07T00:03:23.977 回答
3

因为文档compareTo声明它应该抛出一个NullPointerException,所以您应该遵循这些准则,以便您的实现与接口文档一致。这也处理了非空字符串在字典上是否小于或大于null.

你有几个关于如何处理这个问题的选择。如果空和不适用不同,那么您可能应该将字符串字段包装在您自己的字段类中。例如,假设您可以创建一种可能具有isApplicable方法的 MyField 类型,该方法指示该字段是否适用于这种情况(或类似的东西)。或者您可以重新考虑您的设计,并确保空字符串和 N/A 确实是两个不同的东西。如果是,您确实需要一种方法来区分两者。

于 2011-06-07T00:05:13.503 回答
3

您需要确定 null 是大于还是小于非 null 值。您可以设计compareTo以满足您的类的自然排序的需要,因此这是不错的做法。

于 2011-06-07T00:08:44.383 回答
0

除了Paulo Ebermann接受的答案之外,如果您需要处理ASC/DESC订单,您会这样做。(我们假设 NULL 总是按正常升序排列在 Non-NULLs 之前,否则按降序排列。)

    final boolean sortAsc = false; // Suppose this TRUE/FALSE is for ASC/DESC
    
    objects.sort(new Comparator<Example>() {

        @Override
        public int compare(Example e1, Example e2) {

            if (e1.getMember() == null && e2.getMember() == null) {
                return 0; // Both NULLs are equal
            }
            else if (e1.getMember() == null && e2.getMember() != null) {
                return sortAsc ? -1 : 1; // NULLs should precede non-NULLs in ascending order, follow in descending order
            }
            else if (e1.getMember() != null && e2.getMember() == null) {
                return sortAsc ? 1 : -1; // Non-NULLs should follow NULLs in ascending order, precede in descending order
            } else {
                // Both non-NULLs
                return sortAsc ? e1.getMember().compareTo(e2.getMember()) 
                               : e2.getMember().compareTo(e1.getMember());
            }
        }
        
    });
于 2020-11-16T16:55:44.403 回答