我们有一些代码会根据坐标之间的距离对地址列表进行排序。这是通过collections.sort和自定义比较器完成的。
但是,有时会在列表中出现没有坐标的地址,从而导致出现NullPointerException。我最初的想法是让比较器返回0作为地址的距离,其中至少有一个坐标为空。我担心这可能会导致列表中“有效”元素的破坏。
所以在比较器确定中为空数据返回'0'值,或者有更简洁的方法来解决这个问题。
答案 0 :(得分:74)
处理它就像null
意味着无限远。因此:
comp(1234, null) == -1
comp(null, null) == 0
comp(null, 1234) == 1
通过这种方式,您可以获得一致的订购。
答案 1 :(得分:22)
为了扩展WilliSchönborn的回答,我来到这里说google-collections正是你在这里所追求的。
在一般情况下,您可以编写自己的Comparator
来忽略空值(假设非空,因此它可以专注于重要的逻辑),然后使用Ordering来处理空值:
Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast());
但是,在您的情况下,它是用于排序的地址(坐标)中的数据,对吗?在这种情况下,google-collections甚至可以更多。所以你可能会有更多的东西:
// Seems verbose at first glance, but you'll probably find yourself reusing
// this a lot and it will pay off quickly.
private static final Function<Address, Coordinates> ADDRESS_TO_COORDINATES =
new Function<Address, Coordinates>() {
public Coordinates apply(Address in) {
return in.getCoordinates();
}
};
private static final Comparator<Coordinates> COORDINATE_SORTER = .... // existing
然后当你想要排序:
Collections.sort(addresses,
Ordering.from(COORDINATE_SORTER)
.nullsLast()
.onResultOf(ADDRESS_TO_COORDINATES));
这就是谷歌收藏的力量真正开始得到回报的地方。
答案 2 :(得分:8)
我对此的看法是,你试图“做好”null
坐标的任何事情只是在克服裂缝。你真正需要做的是找到并修复注入虚假null
坐标的错误。
根据我的经验,NPE错误的侵扰通常是由以下不良编码习惯引起的:
null
来避免创建空数组或集合,null
,或null
来表示“无价值”。(对“无价值”问题的更好解决方案通常涉及重写代码,以便您不需要来表示此和/或使用非空值;例如,空字符串,一个特殊的实例,一个保留的值。你不能总是找到一个更好的解决方案,但你经常可以。)
如果这描述了您的应用程序,您应该花时间根除代码问题,而不是考虑隐藏NPE的方法。
答案 3 :(得分:8)
我的解决方案(对于看这里的人可能有用)是做比较法线,空值替换不是0,但可能是最大值(例如Integer.MAX_VALUE)。如果您的值本身为0,则返回0不一致。这是一个正确的示例:
public int compare(YourObject lhs, YourObject rhs) {
Integer l = Integer.MAX_VALUE;
Integer r = Integer.MAX_VALUE;
if (lhs != null) {
l = lhs.giveMeSomeMeasure();
}
if (rhs != null) {
r = rhs.giveMeSomeMeasure();
}
return l.compareTo(r);
}
我只想补充一点,你不需要整数的最大值。这取决于你的giveMeSomeMeasure()方法可以返回什么。例如,如果你比较天气的摄氏度,你可以将l和r设置为-300或+300,具体取决于你想要设置空对象的位置 - 列表的头部或尾部。
答案 4 :(得分:3)
你可能不想返回0因为这意味着地址是等距的,你真的不知道。在您尝试处理错误的输入数据时,这是一个非常典型的问题。当你 不知道 距离时,我不认为比较器有责任尝试确定地址在实际中的距离。我会在排序之前从列表中删除这些地址。
黑客将把它们移到列表的底部(但那太丑了!)
答案 5 :(得分:3)
如果您使用的是Java 8,那么Comparator类中有2个新的静态方法,它们会派上用场:
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)
比较将为null安全,您可以选择将空值放在已排序序列中的位置。
以下示例:
List<String> monkeyBusiness = Arrays.asList("Chimp", "eat", "sleep", "", null, "banana",
"throw banana peel", null, "smile", "run");
Comparator<? super String> comparator = (a, b) -> a.compareTo(b);
monkeyBusiness.stream().sorted(Comparator.nullsFirst(comparator))
.forEach(x -> System.out.print("[" + x + "] "));
将打印: [null] [null] [] [黑猩猩] [香蕉] [吃] [运行] [睡觉] [微笑] [扔香蕉皮]
答案 6 :(得分:1)
不,没有更清洁的方式。也许:
但更重要的是 - 尝试摆脱/填写缺失的坐标,或者更好的是:不要在列表中放置缺少坐标的地址。
实际上,不将它们列入列表是最合乎逻辑的行为。如果将它们放在列表中,结果实际上不会按距离排序。
您可以创建另一个列表,其中包含缺少坐标的地址,并向需要该信息的人(最终用户,API用户)明确说明第一个列表仅包含具有所需数据的地址,而第二个列表包含缺少必要信息的地址。
答案 7 :(得分:1)
不要把它看作是比较器的技术问题,最好再看一下这些要求:你真正想做的是什么,你打算用这个排序列表做什么? / p>
正如你已经意识到的那样,当其中一个为空时总是返回0并不是一个好主意;它确实可以破坏结果。但是你应该做的事情取决于你需要什么,而不是取决于其他人通常做什么/需要什么。您的程序如何使用没有位置的地址(用户将要看到的内容)不应该依赖于某些技术细节,例如比较器的“最佳实践”。 (对我来说,问一下“最佳实践”是什么,听起来好像在问“最佳要求”是什么。)
答案 8 :(得分:1)
我个人讨厌在我的比较器中到处处理特殊的空案例,所以我正在寻找更清洁的解决方案,最后找到谷歌收藏品。他们的订购很棒。它们支持复合比较器,提供将空值排序到顶部和结尾,并允许在比较之前运行某些功能。编写比较器从未如此简单。你应该试一试。