1

I am sorting my custom objects. Custom object contain state and timestamp. First I have to sort against state then timestamp. State having below values

Running, Waiting, Registering, Completed and Aborted

So state having value Running should come on top then Waiting and so on.

If I have to sort alphabetically then I can do easily via

state1.compareTo(state2)

But can I sort with this criteria. Please help me to write this logic.

EDIT

As you people suggested I took Enum

private enum TournamentState {
    Running,
    Waiting,
    Registering,
    Completed,
    Aborted
}

And compare like below

int sort = EnumState.valueOf(status1).compareTo(EnumState.valueOf(status2));

if(sort != 0){
  return sort;
}else{
   return (int) (time1 - time2);
}

Thank you very much for your support.

8
  • 1
    just check if state1.compareTo(state2) returns 0, meaning both states are equal, and then either compare the timestamp for equal states or return the result of the state comparision for unequal states. Commented Jun 18, 2018 at 11:51
  • 1
    You already mention Comparator, what does yours look like right now? Commented Jun 18, 2018 at 11:53
  • but what if state1.compareTo(state2) does not return 0 Commented Jun 18, 2018 at 11:53
  • 2
    I think you might be facing problem ordering records based on state, means which should come first, second and so on. You can declare your state as an enum and try to give them ordinal according to your order and then use the ordinal instead of state name in the comparator. Commented Jun 18, 2018 at 11:54
  • 1
    Put the states in an Enum, in the order you want them to be since Enum already implements Comparable. Sorts the objects by the states and then by the timestamps Commented Jun 18, 2018 at 11:55

3 Answers 3

4

You can use composition of comparator functions:

Comparator<MyClass> comparator = Comparator.comparing(MyClass::getState)
    .thenComparing(MyClass::getTimeStamp);

The last line may need to be changed accordingly, depending on the data type:

.thenComparingLong(MyClass::getTimeStamp); //if it's a long TS

Or even

.thenComparing((ts1, ts2) -> {
    //custom comparison logic for time stamp values
    return result;
 });

Comparator.thenComparing is documented with this comment:

Returns a lexicographic-order comparator with another comparator. If this Comparator considers two elements equal, i.e. compare(a, b) == 0, other is used to determine the order.

Note that MyClass.state is assumed to be comparable in this case, such as being an enum, which is inherently comparable. If it's plain strings, then you may need custom logic there too, such as with:

final String order = "Running, Waiting, Registering, Completed and Aborted";
Comparator<MyClass> comparator = 
  Comparator.comparingInt(e -> order.indexOf(e.getState()))
    .thenComparing(MyClass::getTimeStamp);
Sign up to request clarification or add additional context in comments.

3 Comments

Comparator.comparing(MyClass::getState) will not sort according to the OP requirement if the enum values are not initially in this order. A custom comparator is required.
@davidxxx That's right, good catch. I've edited. But it would be best if the state field is of an enum type.
No you are good : this field is an enum but on what I would stress on is the thenComparing() will sort according to the order of the declaration of the enum values that may not be the expected for this sort (not the case according to the OP update) but that may also change in the time.
2

You can create a custom comparator for your State class, like this :

public final class StateComparator implements Comparator<State>
{

    private int getRank(final State s)
    {
        if (s.getValue().equals("Running"))
        {
            return 1;
        } else if (s.getValue().equals("Waiting")) {
            return 2;
        } else if (s.getValue().equals("Registering")) {
            return 3;
        } else if (s.getValue().equals("Completed")) {
            return 4;
        } else if s.getValue().equals("Aborted") {
            return 5;
        } else {
            return Integer.MAX_VALUE;
        }

    }

    public int compare(final State s1, final State s3)
    {
        return getRank(s1) - getRank(S2);
    }

}

1 Comment

Overly complicated.
0

You can create an enum like

public enum State {
    RUNNING, WAITING, REGISTERING, COMPLETED, ABORTED
}

By default, these states will get an ordinal integer which will be according to the order you write them in the enum. And then use this State as your state in your object and the create comparator

Comparator<MyClass> comparator = Comparator.comparing(MyClass::getState)
    .thenComparing(MyClass::getTimeStamp);

And then you can use the comparator to sort your collections

Comments

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.