views:

693

answers:

8

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.

+2  A: 

Implement 3 different types of Comparator.

you can add the comparator to the sort command. The comparator you define, will sort the elements by name, age, or what ever.

Collections.sort(list, new Comparator() {

        public int compare(Object arg0, Object arg1) {
            if (!(arg0 instanceof Person)) {
                return -1;
            }
            if (!(arg1 instanceof Person)) {
                return -1;
            }

            Person pers0 = (Person)arg0;
            Person pers1 = (Person)arg1;


            // COMPARE NOW WHAT YOU WANT
            // Thanks to Steve Kuo for your comment!
            return pers0.getAge() - pers1.getAge();
        }
    });
Markus Lausberg
why not using parametric type for Comparator?
dfa
because i am coming from 1.4 ;)
Markus Lausberg
You could just return pers0.getAge() - pers1.getAge(). That will work for a three cases (<, > and ==).
Steve Kuo
+10  A: 

Implement the Comparator interface (once for each different sort order) and use the Collections.sort() method that takes a Comparator as additional parameter.

Michael Borgwardt
+1 for providing links
basszero
+1  A: 

The Collections.sort method can be invoked with a second argument which is the comparator to use. Create 3 comparators and use the one you want when appropriate.

Collections.sort(list , new Comparator() {
        public int compare(Object o1, Object o2) {
          ...
        }
      });
z5h
awwww ... Comparator<Person> ... syntatic sugar is tasty!
basszero
+7  A: 

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
}
Andreas_D
+1 I like the idea of a flexible comparator
dfa
You could also pass the sortingBy parameter in a constructor.
Knut Arne Vedaa
+1  A: 

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());
Knut Arne Vedaa
+2  A: 

You could also use the BeanComparator from apache commons beanutils, like this:

Collections.sort(personList, new BeanComparator("name"));
Jörn Horstmann
A: 

I asked a very similar question (about searching rather than sorting), perhaps there is some useful information (I ended up using an enum that implements Comparator so I pass the enum value as a comparator selector).

Jason S
+1  A: 

Using lambdaj ( http://code.google.com/p/lambdaj/ ) you can achieve what you're asking in the following way:

sort(personList, on(Person.class).getName());

sort(personList, on(Person.class).getAge());

sort(personList, on(Person.class).getCountry());

Mario Fusco