无论如何,将空值排序到底部的一般方法很好吗?
我正在编写一些自定义比较器,我希望它们将空项目推送到列表的底部,无论我是按升序还是降序排序。 接近这个的好策略或模式是什么?
随口说说:
- 只需编写单独的升序和降序比较器,尽可能共享代码
- 通过抛出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.nullsLast
和Comparator.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要长。