我正在编写一些自定义比较器,我希望它们将空项目推到列表的底部,无论我是按升序还是降序排序。解决这个问题的好策略或模式是什么?
副手:
- 只需编写单独的升序和降序比较器,尽可能共享代码
- 通过抛出 NPE 或显式调用它,将 null 处理委托给另一个类
- 包括一个升旗并在其中放置条件逻辑以绕过空值
- 在空处理类中包装常规比较器
还有其他策略吗?我想听听不同方法的任何经验,以及各种策略的任何陷阱。
我正在编写一些自定义比较器,我希望它们将空项目推到列表的底部,无论我是按升序还是降序排序。解决这个问题的好策略或模式是什么?
副手:
还有其他策略吗?我想听听不同方法的任何经验,以及各种策略的任何陷阱。
我同意 Jon Skeet(这很容易:)。我尝试实现一个非常简单的装饰器:
class NullComparators {
static <T> Comparator<T> atEnd(final Comparator<T> comparator) {
return new Comparator<T>() {
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 <T> Comparator<T> atBeginning(final Comparator<T> comparator) {
return Collections.reverseOrder(atEnd(comparator));
}
}
给定一个比较器:
Comparator<String> wrapMe = new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
和一些测试数据:
List<String> 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));
[空,空,空,ccc,bbb,aaa]
最后一个选项对我很有吸引力。比较器非常适合链接在一起。特别是你可能想写 aReverseComparator
和NullWrappingComparator
.
编辑:你不必自己写这个。如果您查看Google Collections Library中的Ordering类,您会发现这个和其他各种好东西 :)
编辑:更详细地说明我的意思ReverseComparator
......
一个警告词 - 在 a 的实现中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
.
跟进 dfa 的回答 - 我想要的是空值在最后排序而不影响非空值的顺序。所以我想要更多类似的东西:
public class NullComparatorsTest extends TestCase {
Comparator<String> forward = new Comparator<String>() {
public int compare(String a, String b) {
return a.compareTo(b);
}
};
public void testIt() throws Exception {
List<String> 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 <T> Comparator<T> atEnd(final Comparator<T> comparator) {
return new Comparator<T>() {
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 <T> Comparator<T> atBeginning(final Comparator<T> comparator) {
return new Comparator<T>() {
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
静态方法来获得对 null 更友好的比较器。假设你有一个Fruit
类似下面的类:
public class Fruit {
private final String name;
private final Integer size;
// Constructor and Getters
}
如果您想按大小对一堆水果进行排序并将null
s 放在最后:
List<Fruit> 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]
你总是可以NullComparator
从 commons-collections 中使用。它的存在时间比 Google Collections 长。