views:

993

answers:

8

In Java, I use a class in which some fields can be null. For example:

class Foo {
    String bar;
    //....
}

I want to write a BarComparator for this class,

    private static class BarComparator
            implements Comparator<Foo> {
        public int compare( final Foo o1, final Foo o2 )
        {
            // Implementation goes here
        }
    }

Is there a standard way to deal with the fact that any of o1, o2, o1.bar, o2.bar can be null, without writing lots of nested if...else?

Cheers!

+1  A: 

It seems to me there is not a method to do it, but anyway the code is not so long.

Enreeco
A: 

I think early return statements would be the other alternative to lots of ifs

e.g.

if(o1==null) return x;
if(o2==null) return x;
if(o1.getBar()==null) return x;
if(o2.getBar()==null) return x;

// No null checks needed from this point.
cagcowboy
+3  A: 

It depends on whether you consider a null entry to be a valid string value worth of comparison. is null < or > "apple". The only thing I could say for sure is that null == null. If you can define where null fits into the ordering then you can write the code appropriately.

In this case I might choose to throw a NullPointerExcpetion or IllegalArgumentException and try to handle the null at a higher level by not putting it in the comparison in the first place.

Matt
+5  A: 

I guess you could wrap the call to the field compareTo method with a small static method to sort nulls high or low:

static <T extends Comparable<T>> int cp(T a, T b) {
     return
         a==null ?
         (b==null ? 0 : Integer.MIN_VALUE) :
         (b==null ? Integer.MAX_VALUE : a.compareTo(b))
}

Simple usage (multiple fields is as you would normally):

    public int compare( final Foo o1, final Foo o2 ) {
        return cp(o1.field, o2.field);
    }
Tom Hawtin - tackline
Off topic I know, but what's the reason to prefer MIN/MAX_VALUE over -/+ 1?
Steve Jessop
Sorry for the delay responding. It's to ensure we have the triangle inequality. For a > b > c, a.compareTo(b) + b.compareTo(c) <= a.compareTo(c). Not that anyone will care...
Tom Hawtin - tackline
A: 

The key thing here is to work out how you would like nulls to be treated. Some options are: a) assume nulls come before all other objects in sort order b) assume nulls come after all other objects in sort order c) treat null as equivalent to some default value d) treat nulls as error conditions. Which one you choose will depend entirely on the application you are working on.

In the last case of course you throw an exception. For the others you need a four-way if/else case (about three minutes of coding one you've worked out what you want the results to be).

DJClayworth
+2  A: 

If you're using Google collections, you may find the Comparators class helpful. If has helper methods for ordering nulls as either the greatest or least elements in the collection. You can use compound comparators to help reduce the amount of code.

Gregg
+1  A: 

Thanks for the replies! The generic method and the Google Comparators look interesting.

And I found that there's a NullComparator in the Apache Commons Collections (which we're currently using):

private static class BarComparator
        implements Comparator<Foo>
{
    public int compare( final Foo o1, final Foo o2 )
    {
        // o1.bar & o2.bar nulleness is taken care of by the NullComparator.
        // Easy to extend to more fields.
        return NULL_COMPARATOR.compare(o1.bar, o2.bar);
    }

    private final static NullComparator NULL_COMPARATOR =
                                            new NullComparator(false);
}

Note: I focused on the bar field here to keep it to the point.

Sébastien RoccaSerra
A: 

You should not use the NullComparator the way you do - you're creating a new instance of the class for every comparison operation, and if e.g. you're sorting a list with 1000 entries, that will be 1000 * log2(1000) objects that are completely superfluous. This can quickly get problematic.

Either subclass it, or delegate to it, or simply implement your own null check - it's really not that complex:

private static class BarComparator
        implements Comparator<Foo> {
    private NullComparator delegate = new NullComparator(false);

    public int compare( final Foo o1, final Foo o2 )
    {
        return delegate.compare(o1.bar, o2.bar);
    }
}
Martin Probst
You're right, the NullComparator should be a private static field. I wrote it that way in the example to keep focus on the nullity.
Sébastien RoccaSerra