关于java:关于null的Comparable和Comparator契约

Comparable and Comparator contract with regards to null

Comparable合同规定e.compareTo(null)必须抛出NullPointerException

从API:

Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.

另一方面,Comparatorapi在比较null时没有提到需要发生什么。考虑下面的一个泛型方法的尝试,该方法使用Comparable,并为它返回一个Comparator,该方法将null作为最小元素。

1
2
3
4
5
6
7
8
9
10
static <T extends Comparable<? super T>> Comparator<T> nullComparableComparator() {
   return new Comparator<T>() {
      @Override public int compare(T el1, T el2) {
         return
            el1 == null ? -1 :
            el2 == null ? +1 :
            el1.compareTo(el2);
      }
   };
}

这使我们可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<Integer> numbers = new ArrayList<Integer>(
   Arrays.asList(3, 2, 1, null, null, 0)
);
Comparator<Integer> numbersComp = nullComparableComparator();
Collections.sort(numbers, numbersComp);
System.out.println(numbers);
//"[null, null, 0, 1, 2, 3]"

List<String> names = new ArrayList<String>(
   Arrays.asList("Bob", null,"Alice","Carol")
);
Comparator<String> namesComp = nullComparableComparator();
Collections.sort(names, namesComp);
System.out.println(names);
//"[null, Alice, Bob, Carol]"

所以问题是:

  • 这是可以接受的使用Comparator,还是违反了比较null和丢弃NullPointerException的不成文规则?
  • 对包含null元素的List进行排序是一个好主意,还是这是设计错误的一个确定标志?


Comparable不允许null仅仅因为:

1
a.compareTo(b) == -b.compareTo(a)

对于所有对象ab,其中!a.equals(b)。更具体地说:

1
2
3
4
a.equals(b) ? b.equals(a) && a.compareTo(b) == 0 &&
                  b.compareTo(a) == 0 && a.hashCode() == b.hashCode()
            : !b.equals(a) && a.compareTo(b) != 0 &&
                  a.compareTo(b) == -b.compareTo(a)

必须对true进行评估,以满足相关合同。

所以不允许使用null,因为你不能这样做:

1
null.compareTo(a)

Comparator更加灵活,因此处理null是一个具体的实施问题。是否支持它取决于您希望您的Comparator做什么。


Is it ever a good idea to even have to sort a List containing null elements, or is that a sure sign of a design error?

从概念上讲,空意味着"没有",在列表中放置任何东西对我来说都是奇怪的。此外,Java列表约定声明

Some list implementations have restrictions on the elements that they may contain. For example, some implementations prohibit null elements

因此,甚至不需要支持Java元素来支持空元素。综上所述,如果您没有很好的理由将空值放入一个列表中,那么就不要这样做,如果这样做了,那么就测试它是否实际按预期工作。


Is it ever a good idea to even have to
sort a List containing null elements,
or is that a sure sign of a design
error?

好吧,列表包含空对象可能没有意义,但是您的列表可能包含一个"业务对象",您可以根据业务对象的不同属性进行排序,其中一些属性可能包含空值。

Is this an acceptable use of a
Comparator

BeanComparator允许您对业务对象中的属性进行排序,即使该属性包含空值,所以我不得不说它是Comparator的一种可接受的用法。