2

I have a question on effective use of Java Comparator.

class MyClass {
    //Active State: OPEN, PENDING, RUNNING
    private String state;
    private Date startDate;
    private Date endDate;
}

Here the possible values of state field are OPEN, PENDING, RUNNING, CLOSED, CANCELLED etc., out of which OPEN, PENDING and RUNNING are the Active state. Now I want to write comparator which sorts the List<MyClass>, so that the active ones comes first and are sorted by startDate followed by non-active ones which are sorted based on endDate.

static final Set<String> ACTIVE;// this set contains OPEN, PENDING, RUNNING

List<MyClass> myList;//This is my list
...
Collections.sort(myList, new Comparator<MyClass>() {
    @Override
    public int compare(MyClass o1, MyClass o2) {
        int c;
        boolean isO2 = ACTIVE.contains(o2.getState());
        boolean isO1 = ACTIVE.contains(o1.getState());
        if (isO2 && isO1) {
            c = DateTimeComparator.getInstance().compare(o2.getStartDate(), o1.getStartDate());
        } else if (isO2) {
            c = 1;
        } else if (isO1) {
            c = -1;
        } else {
            c = DateTimeComparator.getInstance().compare(o2.getEndDate(), o1.getEndDate());
        }
        return c;
    }
});

My question is whether my above implementation of having single comparator good? or are there better ways to do it? Most likely I have to stick with Java 7 but solutions with Java 8 are welcome too.

3
  • 2
    Question: why is your active state a String? Wouldn't an enum be more appropriate here? Commented Jan 4, 2017 at 2:01
  • BTW: I don't have issue with your implementation but you'd best wait for someone more expert in this to fully weigh in. Commented Jan 4, 2017 at 2:07
  • @HovercraftFullOfEels, Unfortunately I can not make any change in MyClass as we don't own it, just simply using it. May be I need to ask the corresponding team to make it enum. Commented Jan 4, 2017 at 2:17

1 Answer 1

4

In Java 8, I think it would be a bit cleaner to use Comparator::comparing. For example:

Comparator<MyClass> comparator = Comparator.nullsFirst(Comparator.comparing((MyClass myClass) -> !isActive(myClass))
    .thenComparing((MyClass myClass) -> isActive(myClass) ? myClass.startDate : myClass.endDate, Comparator.nullsFirst(DateTimeComparator.getInstance())));

private static boolean isActive(MyClass myClass)
{
    switch (myClass.state)
    {
    case "OPEN":
    case "PENDING":
    case "RUNNING":
        return true;
    default:
        return false;
    }
}

In Java 7, assuming you have Guava on the classpath, you could use Ordering. For example:

Comparator<MyClass> comparator = Ordering.natural().reverse().onResultOf(new Function<MyClass, Boolean>() {
        @Override
        public Boolean apply(MyClass myClass) {
            return isActive(myClass);
        }
    })
    .compound(Ordering.from(DateTimeComparator.getInstance()).nullsFirst().onResultOf(new Function<MyClass, Date>() {
        @Override
        public Date apply(MyClass myClass) {
            return isActive(myClass) ? myClass.startDate : myClass.endDate;
        }
    }))
    .nullsFirst();
Sign up to request clarification or add additional context in comments.

7 Comments

Is there an equivalent of this in Java 7? This is good though. +1
How will the dates the compared here? In my solution I am using DateTimeComparator. What will he be used here if the dates are joda time?
You can pass your DateTimeComparator as the second parameter to thenComparing. See the updated code.
I don't think there's a particularly elegant solution in Java 7 without the use of Guava or another library.
I added an example in Java 7, though unfortunately it requires Guava.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.