我正在为这样的简单类实现compareTo()方法(以便能够使用Collections.sort()和Java平台提供的其他功能):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
我希望这些对象的自然顺序是:1)按名称排序,2)如果名称相同则按值排序; 两个比较都应该不区分大小写。 对于这两个字段,空值是完全可以接受的,因此在这些情况下 compareTo
一定不能中断。
想到的解决方案如下(我在这里使用“保护条款”,而其他人可能更喜欢单个返回点,但这不是重点):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
这可以完成工作,但我并不完美对这段代码很满意。 诚然,它并不是非常复杂,但相当冗长乏味。
问题是,如何使其不那么冗长(同时保留功能)? 如果有帮助,请随意参考 Java 标准库或 Apache Commons。 使这个(稍微)更简单的唯一选择是实现我自己的“NullSafeStringComparator”,并将其应用于比较两个字段吗?
编辑1-3:埃迪是对的; 修复了上面的“两个名字都为空”的情况
关于接受的答案
我在 2009 年问过这个问题,当然是在 Java 1.6 上,当时 Eddie 的纯 JDK 解决方案 是我首选的接受答案。 直到现在(2017 年)我才抽出时间来改变这一点。
还有第三方库解决方案——2009 Apache Commons Collections 和 2013 Guava 的解决方案,均由我发布——在某个时间点我确实更喜欢。
我现在将 Lukasz Wiktor 提供的干净的 Java 8 解决方案作为可接受的答案。 如果在 Java 8 上,这绝对是首选,而且现在 Java 8 应该可用于几乎所有项目。
I'm implementing compareTo()
method for a simple class such as this (to be able to use Collections.sort()
and other goodies offered by the Java platform):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
I want the natural ordering for these objects to be: 1) sorted by name and 2) sorted by value if name is the same; both comparisons should be case-insensitive. For both fields null values are perfectly acceptable, so compareTo
must not break in these cases.
The solution that springs to mind is along the lines of the following (I'm using "guard clauses" here while others might prefer a single return point, but that's beside the point):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
This does the job, but I'm not perfectly happy with this code. Admittedly it isn't very complex, but is quite verbose and tedious.
The question is, how would you make this less verbose (while retaining the functionality)? Feel free to refer to Java standard libraries or Apache Commons if they help. Would the only option to make this (a little) simpler be to implement my own "NullSafeStringComparator", and apply it for comparing both fields?
Edits 1-3: Eddie's right; fixed the "both names are null" case above
About the accepted answer
I asked this question back in 2009, on Java 1.6 of course, and at the time the pure JDK solution by Eddie was my preferred accepted answer. I never got round to changing that until now (2017).
There are also 3rd party library solutions—a 2009 Apache Commons Collections one and a 2013 Guava one, both posted by me—that I did prefer at some point in time.
I now made the clean Java 8 solution by Lukasz Wiktor the accepted answer. That should definitely be preferred if on Java 8, and these days Java 8 should be available to nearly all projects.
发布评论
评论(18)
您可以简单地使用 Apache Commons Lang:
You can simply use Apache Commons Lang:
使用Java 8:
Using Java 8:
我会实现一个空安全比较器。 可能有一个实现,但实现起来非常简单,所以我总是推出自己的实现。
注意:如果两个名称都为空,上面的比较器甚至不会比较值字段。 我认为这不是你想要的。
我将通过以下内容来实现这一点:
编辑:修复了代码示例中的拼写错误。 这就是我没有先测试它所得到的结果!
编辑:将 nullSafeStringComparator 提升为静态。
I would implement a null safe comparator. There may be an implementation out there, but this is so straightforward to implement that I've always rolled my own.
Note: Your comparator above, if both names are null, won't even compare the value fields. I don't think this is what you want.
I would implement this with something like the following:
EDIT: Fixed typos in code sample. That's what I get for not testing it first!
EDIT: Promoted nullSafeStringComparator to static.
请参阅此答案的底部,了解使用 Guava 的更新(2013)解决方案。
这就是我最终选择的。 事实证明,我们已经有了一个用于空安全字符串比较的实用方法,因此最简单的解决方案就是利用它。 (这是一个很大的代码库;很容易错过这种事情:)
这就是帮助器的定义方式(它是重载的,因此您还可以定义空值是第一个还是最后一个,如果您愿意的话):
所以这本质上与埃迪的回答(虽然我不会将静态辅助方法调用为比较器)和也是 uzhin。
无论如何,总的来说,我强烈支持 帕特里克的解决方案,因为我认为尽可能使用已建立的库是一个很好的做法。 (了解并使用库,正如 Josh Bloch 所说。)但在这种情况下,这不会产生最干净、最简单的代码。
编辑(2009):Apache Commons Collections版本
实际上,这是一种基于Apache Commons制定解决方案的方法
NullComparator
更简单。 将其与不区分大小写的比较器结合使用
在String
类中提供:我认为现在这非常优雅。 (只剩下一个小问题:Commons
NullComparator
不支持泛型,因此存在未经检查的分配。)更新(2013):Guava 版本
近 5 年后,以下是我如何处理我的原始版本问题。 如果用 Java 编码,我(当然)会使用 Guava< /a>. (当然不是 Apache Commons。)
将此常量放在某个地方,例如在“StringUtils”类中:
然后,在
public class Metadata Implements Comparable
中:当然,这与 Apache Commons 版本几乎相同(都使用
JDK的CASE_INSENSITIVE_ORDER),使用
nullsLast()
是唯一特定于 Guava 的东西。 这个版本更可取,只是因为 Guava 作为依赖项比 Commons Collections 更可取。 (正如每个人都同意。)如果您想知道
订购
,请注意它实现了Comparator
。 它非常方便,特别是对于更复杂的排序需求,例如允许您使用compound()
链接多个排序。 请阅读订购说明了解更多信息!See the bottom of this answer for updated (2013) solution using Guava.
This is what I ultimately went with. It turned out we already had a utility method for null-safe String comparison, so the simplest solution was to make use of that. (It's a big codebase; easy to miss this kind of thing :)
This is how the helper is defined (it's overloaded so that you can also define whether nulls come first or last, if you want):
So this is essentially the same as Eddie's answer (although I wouldn't call a static helper method a comparator) and that of uzhin too.
Anyway, in general, I would have strongly favoured Patrick's solution, as I think it's a good practice to use established libraries whenever possible. (Know and use the libraries as Josh Bloch says.) But in this case that would not have yielded the cleanest, simplest code.
Edit (2009): Apache Commons Collections version
Actually, here's a way to make the solution based on Apache Commons
NullComparator
simpler. Combine it with the case-insensitiveComparator
provided inString
class:Now this is pretty elegant, I think. (Just one small issue remains: the Commons
NullComparator
doesn't support generics, so there's an unchecked assignment.)Update (2013): Guava version
Nearly 5 years later, here's how I'd tackle my original question. If coding in Java, I would (of course) be using Guava. (And quite certainly not Apache Commons.)
Put this constant somewhere, e.g. in "StringUtils" class:
Then, in
public class Metadata implements Comparable<Metadata>
:Of course, this is nearly identical to the Apache Commons version (both use
JDK's CASE_INSENSITIVE_ORDER), the use of
nullsLast()
being the only Guava-specific thing. This version is preferable simply because Guava is preferable, as a dependency, to Commons Collections. (As everyone agrees.)If you were wondering about
Ordering
, note that it implementsComparator
. It's pretty handy especially for more complex sorting needs, allowing you for example to chain several Orderings usingcompound()
. Read Ordering Explained for more!我总是建议使用 Apache commons,因为它很可能比您自己编写的更好。 另外,您可以做“真正的”工作,而不是重新发明。
您感兴趣的课程是 空比较器。 它允许您将空值调高或调低。 您还可以为其提供自己的比较器,以便在两个值不为空时使用。
在你的情况下,你可以有一个静态成员变量来进行比较,然后你的compareTo方法只引用它。
类
即使您决定推出自己的
,也请记住此类,因为它在对包含空元素的列表进行排序时非常有用。
I always recommend using Apache commons since it will most likely be better than one you can write on your own. Plus you can then do 'real' work rather then reinventing.
The class you are interested in is the Null Comparator. It allows you to make nulls high or low. You also give it your own comparator to use when the two values are not null.
In your case you can have a static member variable that does the comparison and then your
compareTo
method just references that.Somthing like
}
Even if you decide to roll your own, keep this class in mind since it is very useful when ordering lists thatcontain null elements.
我知道这可能不能直接回答您的问题,因为您说必须支持空值。
但我只是想指出,在compareTo中支持空值并不符合官方用于比较的 javadocs:
因此,我要么显式抛出 NullPointerException,要么在取消引用空参数时让它第一次抛出。
I know that it may be not directly answer to your question, because you said that null values have to be supported.
But I just want to note that supporting nulls in compareTo is not in line with compareTo contract described in official javadocs for Comparable:
So I would either throw NullPointerException explicitly or just let it be thrown first time when null argument is being dereferenced.
您可以提取方法:
}
You can extract method:
}
您可以将您的类设计为不可变的(Effective Java 第 2 版有一个很好的章节,第 15 项:最小化可变性),并确保在构造时不可能出现空值(并使用 空对象模式(如果需要)。 然后您可以跳过所有这些检查并安全地假设这些值不为空。
You could design your class to be immutable (Effective Java 2nd Ed. has a great section on this, Item 15: Minimize mutability) and make sure upon construction that no nulls are possible (and use the null object pattern if needed). Then you can skip all those checks and safely assume the values are not null.
输出是
output is
我一直在寻找类似的东西,这看起来有点复杂,所以我这样做了。 我觉得这样比较容易理解一些。 您可以将其用作比较器或单衬管。 对于这个问题,您将更改为compareToIgnoreCase()。 照原样,空值会向上浮动。 如果你想让它们沉下去,你可以翻转 1、-1。
。
I was looking for something similar and this seemed a bit complicated so I did this. I think it's a little easier to understand. You can use it as a Comparator or as a one liner. For this question you would change to compareToIgnoreCase(). As is, nulls float up. You can flip the 1, -1 if you want them to sink.
.
如果有人使用 Spring,有一个类 org.springframework.util.comparator.NullSafeComparator 也可以为您完成此操作。 只需用它装饰你自己的比较
new NullSafeComparator(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/ NullSafeComparator.html
In case anyone using Spring, there is a class org.springframework.util.comparator.NullSafeComparator that does this for you as well. Just decorate your own comparable with it like this
new NullSafeComparator<YourObject>(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/NullSafeComparator.html
我们可以使用 java 8 在对象之间进行空友好比较。
假设我有一个包含 2 个字段的 Boy 类:字符串名称和整数年龄,我想首先比较名称,然后比较年龄(如果两者相等)。
结果:
we can use java 8 to do a null-friendly comparasion between object.
supposed i hava a Boy class with 2 fields: String name and Integer age and i want to first compare names and then ages if both are equal.
and the result:
对于您知道数据不会有空值(对于字符串来说总是一个好主意)并且数据非常大的特定情况,您仍然在实际比较值之前进行三个比较,如果您确定这是你的情况,你可以稍微优化一下。 YMMV 作为可读代码胜过小的优化:
For the specific case where you know the data will not have nulls (always a good idea for strings) and the data is really large, you are still doing three comparisons before actually comparing the values, if you know for sure this is your case, you can optimize a tad bit. YMMV as readable code trumps minor optimization:
使用 NullSafe Comparator 的简单方法之一 就是使用Spring实现它,下面是一个简单的例子可以参考:
One of the simple way of using NullSafe Comparator is to use Spring implementation of it, below is one of the simple example to refer :
另一个 Apache ObjectUtils 示例。 能够对其他类型的对象进行排序。
Another Apache ObjectUtils example. Able to sort other types of objects.
这是我用来对 ArrayList 进行排序的实现。 空类被排序到最后。
对于我的情况,EntityPhone 扩展了 EntityAbstract,我的容器是 List < 实体摘要>。
“compareIfNull()”方法用于空安全排序。 其他方法是为了完整性,展示了如何使用compareIfNull。
This is my implementation that I use to sort my ArrayList. the null classes are sorted to the last.
for my case, EntityPhone extends EntityAbstract and my container is List < EntityAbstract>.
the "compareIfNull()" method is used for null safe sorting. The other methods are for completeness, showing how compareIfNull can be used.
可以处理 null 方面并在自定义
compareTo
方法实现中使用的通用实用程序类可能如下所示:A generic utility class which can handle the null aspect and be used in a custom
compareTo
method implementation may look like this:如果你想要一个简单的黑客:
如果你想将空值放在列表的末尾,只需在上面的方法中更改它
If you want a simple Hack:
if you want put nulls to end of the list just change this in above metod