Sorting a collection of objects [duplicate]
If I have a simple list of Strings:
List<String> stringList = new ArrayList<String>();
I can sort it with:
Collections.sort(stringList);
But suppose I have a Person class:
public class Person
{
private String name;
private Integer age;
private String country;
}
And a list of it:
List<Person> personList = new ArrayList<Person>();
And I want to sort it sometimes by name, sometimes by age, sometimes by country.
What is the easiest way to accomplish that?
I know that I can implement the Comparable interface, but that seems to limit me to sort it by one specific property.
Solution 1:
Collections.sort can be called with a custom comparator. And that comparator can be implemented to allow sorting in different sort orders. Here's an example (for your Person model - with age as an Integer):
public class FlexiblePersonComparator implements Comparator<Person> {
public enum Order {Name, Age, Country}
private Order sortingBy = Name;
@Override
public int compare(Person person1, Person person2) {
switch(sortingBy) {
case Name: return person1.name.compareTo(person2.name);
case Age: return person1.age.compareTo(person2.age);
case Country: return person1.country.compareTo(person2.country);
}
throw new RuntimeException("Practically unreachable code, can't be thrown");
}
public void setSortingBy(Order sortBy) {
this.sortingBy = sortingBy;
}
}
And you use it like that (assuming persons is a field):
public void sortPersonsBy(FlexiblePersonComparator.Order sortingBy) {
List<Person> persons = this.persons; // useless line, just for clarification
FlexiblePersonComparator comparator = new FlexiblePersonComparator();
comparator.setSortingBy(sortingBy);
Collections.sort(persons, comparator); // now we have a sorted list
}
Solution 2:
Implement the Comparator interface (once for each different sort order) and use the Collections.sort() method that takes a Comparator as additional parameter.
Solution 3:
Thanks to the responders. For the benefit of others, I'd like to include a complete example.
The solution is the create the following additional classes:
public class NameComparator implements Comparator<Person>
{
public int compare(Person o1, Person o2)
{
return o1.getName().compareTo(o2.getName());
}
}
public class AgeComparator implements Comparator<Person>
{
public int compare(Person o1, Person o2)
{
return o1.getAge().compareTo(o2.getAge());
}
}
public class CountryComparator implements Comparator<Person>
{
public int compare(Person o1, Person o2)
{
return o1.getCountry().compareTo(o2.getCountry());
}
}
The list can then be sorted like this:
Collections.sort(personList, new NameComparator());
Collections.sort(personList, new AgeComparator());
Collections.sort(personList, new CountryComparator());
Solution 4:
The Java 8 way of doing this is to use List.sort
as follows:
personList.sort(Comparator.comparing(Person::getName));
To quote Stuart Marks in his answer over here.
This is the big advantage of the
List.sort(cmp)
extension method overCollections.sort(list, cmp)
. It might seem that this is merely a small syntactic advantage being able to writemyList.sort(cmp)
instead ofCollections.sort(myList, cmp)
. The difference is thatmyList.sort(cmp)
, being an interface extension method, can be overridden by the specificList
implementation. For example,ArrayList.sort(cmp)
sorts the list in-place usingArrays.sort()
whereas the default implementation implements the old copyout-sort-copyback technique.