Java 具有空值的比较器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2401606/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
comparator with null values
提问by pvgoddijn
We have some code wich sorts a list of addresses based on the distance between their coordinates. this is done through collections.sort with a custom comparator.
我们有一些代码根据地址之间的坐标距离对地址列表进行排序。这是通过带有自定义比较器的 collections.sort 完成的。
However from time to time an adress without coordinates is in the list causing a NullPointerException. My initial idea to fix this was to have the comparator return 0 as dististance for addresses where at least one of the coordinates is null. I fear this might lead to corruption of the order the 'valid' elements in the list.
然而,列表中有时会出现一个没有坐标的地址,导致 NullPointerException。我最初解决这个问题的想法是让比较器返回 0 作为至少一个坐标为空的地址的距离。我担心这可能会导致列表中“有效”元素的顺序损坏。
so is returning a '0' values for null data in a comparator ok, or is there a cleaner way to resolve this.
所以在比较器中为空数据返回“0”值,或者是否有更清晰的方法来解决这个问题。
采纳答案by Sjoerd
Handle it like null
means infinitely far away. Thus:
处理它就像null
意味着无限远。因此:
comp(1234, null) == -1
comp(null, null) == 0
comp(null, 1234) == 1
comp(1234, null) == -1
comp(null, null) == 0
comp(null, 1234) == 1
With this, you get a consistent ordering.
这样,您将获得一致的排序。
回答by Bozho
No, there is no cleaner way. Perhaps:
不,没有更干净的方法。也许:
- if the coordinates of both compared objects are null, return 0
- if the coordinates of one of the objects are null, return -1 / 1 (depending on whether it's the first or the second argument)
- 如果两个比较对象的坐标都为空,则返回 0
- 如果其中一个对象的坐标为空,则返回 -1 / 1(取决于它是第一个参数还是第二个参数)
But more importantly - try to get rid of / fill in the missing coordinates, or, better: don't put addresses with missing coordinates in the list.
但更重要的是 - 尝试删除/填充缺失的坐标,或者更好:不要将缺少坐标的地址放入列表中。
Actually, not putting them in the list is the most logical behaviour. If you put them in the list, the result won't be actually ordered by distance.
实际上,不将它们放在列表中是最合乎逻辑的行为。如果你把它们放在列表中,结果实际上不会按距离排序。
You may create another list, containing the addresses with missing coordinates, and make it clear to whoever needs that information (end-user, API user), that the first list only contains addresses with the needed data, while the second list contains addresses that lack required information.
您可以创建另一个列表,其中包含缺少坐标的地址,并向需要该信息的任何人(最终用户、API 用户)明确,第一个列表仅包含具有所需数据的地址,而第二个列表包含具有所需数据的地址缺乏必要的信息。
回答by LenW
You probably dont want to return 0 as that implies the addresses are equidistant and you really dont know. This is quite a classic problem where you are trying to deal with bad input data. I dont think its the responsibility of the comparator to try and determin how far the address is in realtive terms when you dont knowthe distance. I would remove those addresses from the list before sorting.
您可能不想返回 0,因为这意味着地址是等距的,而您真的不知道。这是一个非常经典的问题,您试图处理错误的输入数据。当您不知道距离时,我认为比较器没有责任尝试确定地址的实际距离。我会在排序之前从列表中删除这些地址。
The hack would be to move them to the bottom of the list (but thats ugly !)
黑客将把它们移到列表的底部(但那很难看!)
回答by Wouter Coekaerts
Instead of looking at this like it's a technical problem with the comparator, it's better to take a look at the requirements again: what you're really trying to do here, what are you going to do with this sorted list?
与其把这个看成是比较器的技术问题,不如再看看需求:你真正想在这里做什么,你打算用这个排序列表做什么?
- If you are trying to sort them to show the most relevant solutions first to a user, it might be a good idea to put the unknown locations last, so treat it like infinity (returning 0/-1/1 depending on which of them is null).
- If you are going to use this result to draw some graph or do some other calculations that depend on them really being sorted by their distance, then the nulls probably shouldn't have been in there anyways (so either remove them first, or throw an exception if at that point there actually weren't supposed to be any addresses with a null location).
- 如果您尝试对它们进行排序以首先向用户显示最相关的解决方案,那么将未知位置放在最后可能是个好主意,因此将其视为无穷大(返回 0/-1/1,取决于它们中的哪一个是空值)。
- 如果您打算使用这个结果来绘制一些图形或进行一些其他计算,这些计算依赖于它们真的按它们的距离排序,那么空值可能无论如何都不应该在那里(所以要么先删除它们,要么抛出一个如果那时实际上不应该有任何地址为空的地址,则例外)。
As you already realize, always returning 0 when one of them is null is not a good idea here; it can corrupt the result indeed. But what you should do instead depends on what you need, not on what others usually do/need. How your program behaves with addresses that don't have a location (so what the user is going to see) should not depend on some technical detail like what the "best practice" for comparators is. (To me, asking what the "best practice" is here, sounds like asking what the "best requirements" are).
正如您已经意识到的那样,当其中一个为 null 时总是返回 0 在这里不是一个好主意;它确实会破坏结果。但是你应该做什么取决于你需要什么,而不是其他人通常做什么/需要什么。您的程序如何处理没有位置的地址(因此用户将看到什么)不应依赖于某些技术细节,例如比较器的“最佳实践”是什么。(对我来说,询问这里的“最佳实践”是什么,听起来像是询问“最佳要求”是什么)。
回答by Stephen C
My take on this is that anything you try to do to "make good" the null
coordinates is just papering over the cracks. What you really need to do is find and fix the bugs that are injecting the spurious null
coordinates.
我对此的看法是,您尝试做的任何事情来“完善”null
坐标都只是掩盖裂缝。您真正需要做的是找到并修复注入虚假null
坐标的错误。
In my experience, infestations of NPE bugs are frequently caused by the following bad coding habits:
根据我的经验,NPE 漏洞的侵扰通常是由以下不良编码习惯引起的:
- inadequate validation of input parameters,
- using
null
to avoid creating empty arrays or collections, - returning
null
when an exception should have been thrown, or - using
null
to represent "no value" when there is a better solution.
- 输入参数的验证不足,
- 使用
null
,以避免产生空阵列或集合, null
在应该抛出异常时返回,或- 使用
null
时,有一个更好的解决方案来表示“没有价值”。
(Better solutions to the "no value" problem typically involve rewriting code so that you don't needto represent this and/or using a non-null value instead; e.g. an empty String, a special instance, a reserved value. You can't always find a better solution, but you often can.)
(“无值”问题的更好解决方案通常涉及重写代码,这样您就不需要表示它和/或使用非空值来代替;例如空字符串、特殊实例、保留值。您可以并不总能找到更好的解决方案,但您通常可以。)
If this describes your application, you should be spending time to root out the code problems rather than thinking of ways to hide the NPEs.
如果这描述了您的应用程序,您应该花时间来根除代码问题,而不是想办法隐藏 NPE。
回答by whiskeysierra
I personally hate dealing with special null cases everywhere in my comparators so i was looking for a cleaner soluation and finally found google collections. Their Ordering is just awesome. They support compound comparators, offer to sort nulls to the top and to the end and allow to run certain functions before comparing. Writing comparators has never been so easy. You should give it a try.
我个人讨厌在我的比较器中到处处理特殊的空情况,所以我一直在寻找更清洁的解决方案,最终找到了 google 集合。他们的订购真是太棒了。它们支持复合比较器,提供将空值排序到顶部和最后,并允许在比较之前运行某些功能。编写比较器从未如此简单。你应该试一试。
回答by Cowan
Just to expand on Willi Sch?nborn's answer, I came here to say that google-collectionsis exactly what you're after here.
只是为了扩展 Willi Sch?nborn 的回答,我来这里是想说google-collections正是您在这里所追求的。
In the general case, you can just write your own Comparator
to ignore nulls (assume non-null, so it can concentrate on the important logic), and then use Orderingto handle the nulls:
一般情况下,你可以自己写Comparator
忽略空值(假设非空,所以可以专注于重要的逻辑),然后使用Ordering来处理空值:
Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast());
In your case, though, it's data WITHIN the Address (the Coordinates) that is being used to sort, right? google-collections is even moreuseful in this case. So you might have something more like:
但是,在您的情况下,用于排序的是地址(坐标)内的数据,对吗?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
then when you want to sort:
那么当你想排序时:
Collections.sort(addresses,
Ordering.from(COORDINATE_SORTER)
.nullsLast()
.onResultOf(ADDRESS_TO_COORDINATES));
and that's where the power of google-collections really starts to pay off.
这就是 google-collections 的力量真正开始发挥作用的地方。
回答by stoilkov
My solution (might be useful for someone looking here) is to do the comparison normal, with null values replaced not by 0, but the maximum value possible (e.g. Integer.MAX_VALUE). Returning 0 is not consistent, if you have values that are themselves 0. Here a correct example:
我的解决方案(可能对看这里的人有用)是进行正常的比较,用空值替换不是 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);
}
I just wanted to add that you don't necessary need the max value for integer. It depends of what your giveMeSomeMeasure() method can return. If for example you compare Celsius degrees for weather, you can set l and r to -300 or +300, depending where you want to set the null objects - to the head or the tail of the list.
我只是想补充一点,您不需要整数的最大值。这取决于您的 giveMeSomeMeasure() 方法可以返回什么。例如,如果您比较天气的摄氏温度,则可以将 l 和 r 设置为 -300 或 +300,具体取决于要将空对象设置为列表的头部或尾部。
回答by Adrian Cosma
If you are using Java 8, you have 2 new static methods in the Comparator class, which come in handy:
如果您使用的是 Java 8,则 Comparator 类中有 2 个新的静态方法,它们会派上用场:
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)
The comparison will be null safe and you can choose where to place the null values in the sorted sequence.
比较将是空安全的,您可以选择在排序序列中放置空值的位置。
The following example:
下面的例子:
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 + "] "));
will print: [null] [null] [] [Chimp] [banana] [eat] [run] [sleep] [smile] [throw banana peel]
会打印:[null][null][][Chimp][banana][eat][run][sleep][smile][扔香蕉皮]