无论如何,将空值排序到底部的一般方法很好吗?

我正在编写一些自定义比较器,我希望它们将空项目推送到列表的底部,无论我是按升序还是降序排序。 接近这个的好策略或模式是什么?

随口说说:

  • 只需编写单独的升序和降序比较器,尽可能共享代码
  • 通过抛出NPE或通过显式调用来将null处理委托给另一个类
  • 包含一个升序标志并在其中放置条件逻辑以导航空值
  • 在null处理类中包装常规比较器

还有其他策略吗? 我想听听有关不同方法的任何经验,以及各种策略的任何缺陷。

最后一个选项对我很有吸引力。 比较器非常适合链接在一起。 特别是你可能想要编写一个ReverseComparator和一个NullWrappingComparator


编辑:你不必自己写。 如果您查看Google Collections Library中的Ordering类,您会发现这个以及各种其他好东西:)


编辑:进入更多细节,以显示我对ReverseComparator意思…

一个警告词 – 在ReverseComparator的实现中,反转参数的顺序而不是否定结果,否则Integer.MIN_VALUE会自行“反转”。

所以这个实现是错误的(假设original是反向的比较器):

 public int compare(T x, T y) { return -original.compare(x, y); } 

但这是对的:

 public int compare(T x, T y) { return original.compare(y, x); } 

原因是我们总是希望反转比较,但如果original.compare(x, y)返回int.MIN_VALUE ,那么坏比较器将返回int.MIN_VALUE ,这是不正确的。 这是由于有趣的属性int.MIN_VALUE == -int.MIN_VALUE

我同意Jon Skeet(这很容易:)。 我试着实现一个非常简单的装饰器 :

 class NullComparators { static  Comparator atEnd(final Comparator comparator) { return new Comparator() { public int compare(T o1, T o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return 1; } if (o2 == null) { return -1; } return comparator.compare(o1, o2); } }; } static  Comparator atBeginning(final Comparator comparator) { return Collections.reverseOrder(atEnd(comparator)); } } 

鉴于比较者:

 Comparator wrapMe = new Comparator() { public int compare(String o1, String o2) { return o1.compareTo(o2); } }; 

和一些测试数据:

 List strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null); 

您可以在结尾处使用空值排序:

 Collections.sort(strings, NullComparators.atEnd(wrapMe)); 
 [aaa,bbb,ccc,null,null,null]

或者在开始时:

 Collections.sort(strings, NullComparators.atBeginning(wrapMe)); 
 [null,null,null,ccc,bbb,aaa]

跟进dfa的回答 – 我想要的是nulls在最后排序而不影响非null的顺序。 所以我想要更多的东西:

 public class NullComparatorsTest extends TestCase { Comparator forward = new Comparator() { public int compare(String a, String b) { return a.compareTo(b); } }; public void testIt() throws Exception { List strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null); Collections.sort(strings, NullComparators.atEnd(forward)); assertEquals("[aaa, bbb, ccc, null, null, null]", strings.toString()); Collections.sort(strings, NullComparators.atBeginning(forward)); assertEquals("[null, null, null, aaa, bbb, ccc]", strings.toString()); } } public class NullComparators { public static  Comparator atEnd(final Comparator comparator) { return new Comparator() { public int compare(T a, T b) { if (a == null && b == null) return 0; if (a == null) return 1; if (b == null) return -1; return comparator.compare(a, b); } }; } public static  Comparator atBeginning(final Comparator comparator) { return new Comparator() { public int compare(T a, T b) { if (a == null && b == null) return 0; if (a == null) return -1; if (b == null) return 1; return comparator.compare(a, b); } }; } } 

尽管如此,这完全归功于dfa – 这只是对他的工作的一个小修改。

在Java 8中,您可以使用Comparator.nullsLastComparator.nullsFirst静态方法来获得更多无效的比较器。 假设你有一个类如下的Fruit类:

 public class Fruit { private final String name; private final Integer size; // Constructor and Getters } 

如果你想按照它们的大小对一堆水果进行排序,并将null s放在最后:

 List fruits = asList(null, new Fruit("Orange", 25), new Fruit("Kiwi", 5)); 

你可以简单地写:

 Collections.sort(fruits, Comparator.nullsLast(Comparator.comparingInt(Fruit::getSize))); 

结果将是:

 [Fruit{name='Kiwi', size=5}, Fruit{name='Orange', size=25}, null] 

您总是可以使用来自commons-collections的NullComparator 。 它的存在时间比Google Collections要长。