Reputation: 2080
It has been asked before, but I have not found a decent implementation with an explanation.
public int compareTo(Object o)
{
if (this == null || o == null)
{
return 0;
}
Tok tmp = (Tok) o;
if (this.rang < tmp.rang)
{
return -1;
} else if (this.rang > tmp.rang ) {
return 1;
} else {
return 0;
}
}
I read two similar questions that I found yet; they insist on implementing another method. I do not understand why this should not work. The method gets an extra object and it checks if its a valid instance or null
, if null
simply return 0
; what would be the easiest way to implement null-safe compareTo
.
The implementation that worked for me was:
public int compareTo(Object o)
{
if (o == null)
{
return 0;
}
Tok tmp = (Tok) o;
if (this.rang < tmp.rang)
{
return -1;
} else if (this.rang > tmp.rang ) {
return 1;
} else {
return 0;
}
}
It's not the optimal implementation one should look in to what good people posted here as answers. For my particular case this was decent enough as this is never null yet the object received can be null and the initial implementation states if either is null return 0. So if given object is null 0 is returned.
Upvotes: 9
Views: 23858
Reputation: 359786
Personally, I like Guava's Ordering
for null-safe comparing. You can specify #nullsFirst()
or #nullsLast()
to avoid NullPointerException
s.
Other important notes, mostly from the comments:
this
is never null
in JavaComparisonChain
if you're implementing a fine-grained compareTo()
When implementing Comparable
, be sure to specify the type parameter so you get compile-time type safety and don't have to use instanceof
or casts:
class Tok implements Comparable<Tok> {
// snip
public int compareTo(Tok other) {
// snip
}
}
Upvotes: 12
Reputation: 28727
The author is insisting that he does not want to remove null values from his
Tok[].
Here is a soultion that allows to sort with NULL values, and is not violating java contracts
To avoid that, you create a compareTo inside class Tok that violates the compareTo contract, you create an explicit NullSafeComparator:
/**
* This comparator accepts null objects,
* sorts ascending, null values are after non null values.
*/
public static final class NullSafeComparator implements Comparator<Tok> {
public int compare(Tok o1, Tok o2) {
int r1 = Integer.MAX_VALUE;
int r2 = Integer.MAX_VALUE;
if (o1 != null) {
r1 = o1.rang;
}
if (o2 != null) {
r2 = o2.rang;
}
return (r1 < r2 ? -1 : (r1 == r2 ? 0 : 1));
}
}
simplified class Tok (remove the static keyword its is used to define that all inside one unit test class):
public static class Tok {
int rang;
public Tok(int rang) {
this.rang = rang;
}
public String toString() {
return Integer.toString(rang);
}
}
Finally a unit test to show:
public void testSort() {
Tok[] toks = new Tok[5];
toks[0] = new Tok(3);
toks[1] = new Tok(1);
toks[2] = null;
toks[3] = null;
toks[4] = new Tok(2);
Arrays.sort(toks, new NullSafeComparator());
for (Tok tok: toks) {
System.out.println(tok);
}
assertEquals(1, toks[0]);
assertNull(toks[4]);
}
Which will give the following desired result:
1
2
3
null
null
Upvotes: 1
Reputation: 3465
According to the documentation:
Note that null is not an instance of any class, and e.compareTo(null) should
throw a NullPointerException even though e.equals(null) returns false.
So if you implement a null-safe method, its behaviour will be unexpected (aka inconsitent with the documentation and propably with the rest of the API).
Upvotes: 0
Reputation: 136002
Strange as it may seem but it is not safe. Try to add your Tok to TreeSet or TreeMap (as a key) and you will get a NullPointerException. The problem is that TreeSet imlementation is based on TreeMap. When you will try to add(null) underlying map will try to put your null which will result in NPE
Upvotes: 1
Reputation: 28727
I am not satisified with the other answers:
You should NOT check for null in compareTo.
It is requested that it throws a NullPointerException, otheriwse you will mess up your Trees and have difficulties finding why your TreeMap does not work.
A very recommendable method:
public int compareTo(Tok other) {
int thisRang = this.rang;
int otherRang = other.rang;
return (thisRang < otherRang ? -1 : (thisRang == otherRang ? 0 : 1));
}
public int compareTo(Object other) {
return compareTo((Tok)other);
}
Further to make it perfect class Tok should be final! (Otherwise you could have problems when you subclass from Tok. (Sun made that Error in Class Date)
final class Tok {
int rang;
}
Dealing with compare and equals is not always easy, consider using instead of Trees (TreeMap) a HashMap, then you dont have to implement compareTo. You should implement hashCode, where you simply return this.rang.
Finally its is highly recomended, but not mandatory to implement equals()
public boolean equals(Object obj) {
return obj instanceof Tok
&& this.rang() == ((Tok) obj).rang;
}
Upvotes: 4
Reputation: 10045
Comparing two objects can be as null-safe as any other method, the catch here is that a normal method has two parameters but compareTo
receives one and the other one is the object itself.
this
can NEVER be null, that would mean you're executing code in a null
object (no instance). In this case a NullPointerException
is going to be thrown right at the invocation of compareTo
, making impossible the execution of its code.
There are as many approaches as objects, because a comparation can be based in the fields of a class that CAN be null (intended caps to exclude primitive types). So, long story short, your null checks should cover the object you receive as a parameter in compareTo
and the used fields. In addition, if you have an external instance that holds some logic (i.e. an utilities class) you should check if that instance is also null.
As a side note, whatever you return if any involved object is null
must be consistent and documented (you can return -1 or 1, to place nulls at the beginning or at the end). Just avoid returning 0 (that would be the same case as if equals
returned true
for a null
object.
Upvotes: 1
Reputation: 3474
Returning 0 would imply that this
and o
are equal, which isn't true if o
is null. Also, this
will never be null.
It's application-dependent, of course. You may want to have an object that should be equal to null. What you return there is up to you, but if you're looking for a general null safe method, it isn't really ideal.
To be completely generic, I would check if o
is null
and, if so, throw some sort of Exception.
Upvotes: 5