Sort ArrayList of custom Objects by property

1,249,873

Solution 1

Since Date implements Comparable, it has a compareTo method just like String does.

So your custom Comparator could look like this:

public class CustomComparator implements Comparator<MyObject> {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
}

The compare() method must return an int, so you couldn't directly return a boolean like you were planning to anyway.

Your sorting code would be just about like you wrote:

Collections.sort(Database.arrayList, new CustomComparator());

A slightly shorter way to write all this, if you don't need to reuse your comparator, is to write it as an inline anonymous class:

Collections.sort(Database.arrayList, new Comparator<MyObject>() {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
});

Since

You can now write the last example in a shorter form by using a lambda expression for the Comparator:

Collections.sort(Database.arrayList, 
                        (o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

And List has a sort(Comparator) method, so you can shorten this even further:

Database.arrayList.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

This is such a common idiom that there's a built-in method to generate a Comparator for a class with a Comparable key:

Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate));

All of these are equivalent forms.

Solution 2

Classes that has a natural sort order (a class Number, as an example) should implement the Comparable interface, whilst classes that has no natural sort order (a class Chair, as an example) should be provided with a Comparator (or an anonymous Comparator class).

Two examples:

public class Number implements Comparable<Number> {
    private int value;

    public Number(int value) { this.value = value; }
    public int compareTo(Number anotherInstance) {
        return this.value - anotherInstance.value;
    }
}

public class Chair {
    private int weight;
    private int height;

    public Chair(int weight, int height) {
        this.weight = weight;
        this.height = height;
    }
    /* Omitting getters and setters */
}
class ChairWeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getWeight() - chair2.getWeight();
    }
}
class ChairHeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getHeight() - chair2.getHeight();
    }
}

Usage:

List<Number> numbers = new ArrayList<Number>();
...
Collections.sort(numbers);

List<Chair> chairs = new ArrayList<Chair>();
// Sort by weight:
Collections.sort(chairs, new ChairWeightComparator());
// Sort by height:
Collections.sort(chairs, new ChairHeightComparator());

// You can also create anonymous comparators;
// Sort by color:
Collections.sort(chairs, new Comparator<Chair>() {
    public int compare(Chair chair1, Chair chair2) {
        ...
    }
});

Solution 3

For sorting an ArrayList you could use the following code snippet:

Collections.sort(studList, new Comparator<Student>(){
    public int compare(Student s1, Student s2) {
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
    }
});

Solution 4

JAVA 8 lambda expression

Collections.sort(studList, (Student s1, Student s2) ->{
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
});

OR

Comparator<Student> c = (s1, s2) -> s1.firstName.compareTo(s2.firstName);
studList.sort(c)

Solution 5

Yes, you can. There are two options with comparing items, the Comparable interface, and the Comparator interface.

Both of these interfaces allow for different behavior. Comparable allows you to make the object act like you just described Strings (in fact, String implements Comparable). The second, Comparator, allows you to do what you are asking to do. You would do it like this:

Collections.sort(myArrayList, new MyComparator());

That will cause the Collections.sort method to use your comparator for it's sorting mechanism. If the objects in the ArrayList implement comparable, you can instead do something like this:

Collections.sort(myArrayList);

The Collections class contains a number of these useful, common tools.

Share:
1,249,873
Samuel
Author by

Samuel

SOreadytohelp

Updated on July 17, 2020

Comments

  • Samuel
    Samuel almost 4 years

    I read about sorting ArrayLists using a Comparator but in all of the examples people used compareTo which according to some research is a method for Strings.

    I wanted to sort an ArrayList of custom objects by one of their properties: a Date object (getStartDay()). Normally I compare them by item1.getStartDate().before(item2.getStartDate()) so I was wondering whether I could write something like:

    public class CustomComparator {
        public boolean compare(Object object1, Object object2) {
            return object1.getStartDate().before(object2.getStartDate());
        }
    }
    
    public class RandomName {
        ...
        Collections.sort(Database.arrayList, new CustomComparator);
        ...
    }
    
  • BalusC
    BalusC about 14 years
    +1 for mentioning that it should return int and that you'd better to use Date#compareTo() for this. Why this isn't upvoted above the other answer is beyond me. This link may also be useful: Object Ordering Tutorial at Sun.com.
  • BalusC
    BalusC about 14 years
    ...but which you can just override with Comparator :)
  • camickr
    camickr about 14 years
    1, this answer was given 2 hours earlier with working code provided as well. There is no need to repost the same solution and clutter the forum especially since BeanComparator is not a standard class, so its not really a solution if the poster doesn't know what you are talking about. If you like the original suggestion you can upvote it and add a comment if you wish.
  • Leigh
    Leigh about 12 years
    Welcome to stackoverflow. This question was answered some time ago. Before resurrecting old threads, please be sure your response adds something significant to the thread.
  • user387184
    user387184 over 10 years
    I tried that - but when I want to access the comparator class ChairWeightComparator outside in any other class I do not get access to this class (of course not since its not public). Do I need to create a new public ChairWeightComparator class in a separate file? - am I really the first one to try this after 3 years or did I miss sth?
  • assylias
    assylias over 10 years
    @user387184 unfortunately android does not support Java 8, although there may be a workaround (I haven't tested it).
  • Björn
    Björn over 10 years
    @user387184 - simply make it public, and put it in it's own file (preferably it's own package as well) and you'll be able to use it everywhere in you project. No need to create an additional class!
  • user387184
    user387184 over 10 years
    you mean create a new file - not a class and put in the code: "class ChairWeightComparator implements Comparator<Chair> {...." ?
  • Björn
    Björn over 10 years
    @user387184, exactly - but with the key word public in front of class.
  • Kuba
    Kuba about 10 years
    I think the answer best should also include the proper way to do it in Java 8. Collections.sort(list, Comparator.comparing(MyObject::getStartDate)); which reads better and is less error prone. It's very easy to write return o1.getStartDate().compareTo(o1.getStartDate());
  • Jarmez De La Rocha
    Jarmez De La Rocha almost 10 years
    Comparator class should be static :)
  • palantus
    palantus almost 10 years
    @JarmezDeLaRocha: I didn't specify whether the comparator was declared inside another class; if it is, then yes, you would want to make it static.
  • Sorter
    Sorter almost 10 years
  • Holger
    Holger almost 9 years
    Or Collections.sort(studList, Comparator.comparing(Student::getFirstName));
  • bcsb1001
    bcsb1001 over 8 years
    Collections.sort(stdList, Comparator.comparing(SomeClass::getName));
  • Zafer
    Zafer over 8 years
    you can use to compare int values like this "return o1.getDistance()-o2.getDistance();"
  • Alexis C.
    Alexis C. over 8 years
    .. or studList.sort(Comparator.comparing(Student::getFirstName));
  • Sorter
    Sorter over 8 years
    The sort order will always be ascending in your case. I've take care of sort order too in my example. Thanks Gentlemen.
  • Taylor O'Connor
    Taylor O'Connor over 8 years
    what happens when o1.getStartDate() returns null?
  • palantus
    palantus over 8 years
    @Taylor O'Connor: it throws a NullPointerException.
  • shmosel
    shmosel about 8 years
    @Kuba better yet, use List.sort().
  • Wendel
    Wendel about 8 years
    The order of String sort is asc by default when use s1.compareTo(s2) and desc when use s2.compareTo(s1)
  • Arashsoft
    Arashsoft almost 8 years
    Please add explanation to your answer.
  • CharlesW
    CharlesW almost 8 years
    Just compile and run. The code is the comment and explanation.
  • Samir
    Samir almost 8 years
    That sorts but gives double value for each element
  • Samir
    Samir almost 8 years
    That sorts but gives double value for each element how to avoid it
  • Federico Piazza
    Federico Piazza almost 8 years
    @Sam, it shouldn't... it's working as expected. Unless you are using a new version with a bug, which I recommend you to post it in the forum. Anyway, this answer was post prior to java 8, if you use it, then it will much better than using lambdaj
  • Samir
    Samir almost 8 years
    I had to remove even items in a foreach loop other wise it gives me double of each content.
  • Ole V.V.
    Ole V.V. over 7 years
    If you are using Java 8, see if you can get rid of Date in favour of one of the new and more programmer frindly classes in the java.time package.
  • Jim Clermonts
    Jim Clermonts over 6 years
    This solution does not work on Android API <24. Do you guys know a solution for this?
  • Basil Bourque
    Basil Bourque over 5 years
    Seems to be a duplicate of this Answer from 3 years prior.
  • Sahil Chhabra
    Sahil Chhabra over 5 years
    Not exactly, only option 2 here is similar to option 1 in the mentioned answer. I feel two different answers can have atleast some similarities if they are answer to the same question.
  • akshay bhange
    akshay bhange over 5 years
    Call requires API level 24
  • Rajiv
    Rajiv about 5 years
    In case anyone is looking at this because they're on Android API <24, you can use Collections.sort() to sort your lists.
  • JFreeman
    JFreeman almost 4 years
    Nowadays you can use: Collections.sort(Database.arrayList, Comparator.comparing(Object::method));
  • palantus
    palantus almost 4 years
    @JFreeman: Thanks for commenting! I didn't include that version because it's nearly identical to the last version I posted, only a little longer to type.
  • Alex
    Alex over 3 years
    This is not working on my Laptop. It shows int cannot be dereferenced error. How to correct it ?
  • Alex
    Alex over 3 years
    This is not working on my Laptop. It shows int cannot be dereferenced error. How to correct it ?
  • Alex
    Alex over 3 years
    This is not working on my Laptop. It shows int cannot be dereferenced error. How to correct it ?
  • AJW
    AJW over 2 years
    fyi, presentation link is dead.
  • GhostCat
    GhostCat over 2 years
    @AJW Thanks, yeah, there is a reason why I added the code here ... I assumed that the link would break one day. Just no clue where else I could provide that file from .
  • StackLloyd
    StackLloyd over 2 years
    There's a missing, yet extremely important detail: in what order does .compareTo() work? Ascending? Descending?
  • d00d
    d00d over 2 years
    That is java 8 or newer, not java8 or older.