[java] Sort a List of objects by multiple fields

I have a List of Java objects that I want to sort according to more than one field.

public class graduationCeremony {
    String campus;
    String faculty;
    String building;
}

Is it possible to use a Comparator or the Comparable interface to sort the list according to multiple fields? All the examples I have seen sort according to only one field. In other words, one can sort by 'campus' OR 'faculty' OR 'building'. I want to sort by 'campus', then 'faculty', then 'building' (as it exists in SQL: ORDER BY campus, faculty, building)

I think this question has been asked before, but I don't understand the accepted answer. Can someone expand or illustrate this answer?

This question is related to java sorting collections

The answer is


Yes, you absolutely can do this. For example:

public class PersonComparator implements Comparator<Person>
{
    public int compare(Person p1, Person p2)
    {
        // Assume no nulls, and simple ordinal comparisons

        // First by campus - stop if this gives a result.
        int campusResult = p1.getCampus().compareTo(p2.getCampus());
        if (campusResult != 0)
        {
            return campusResult;
        }

        // Next by faculty
        int facultyResult = p1.getFaculty().compareTo(p2.getFaculty());
        if (facultyResult != 0)
        {
            return facultyResult;
        }

        // Finally by building
        return p1.getBuilding().compareTo(p2.getBuilding());
    }
}

Basically you're saying, "If I can tell which one comes first just by looking at the campus (before they come from different campuses, and the campus is the most important field) then I'll just return that result. Otherwise, I'll continue on to compare faculties. Again, stop if that's enough to tell them apart. Otherwise, (if the campus and faculty are the same for both people) just use the result of comparing them by building."


You have to write your own compareTo() method that has the Java code needed to perform the comparison.

If we wanted for example to compare two public fields, campus, then faculty, we might do something like:

int compareTo(GraduationCeremony gc)
{
    int c = this.campus.compareTo(gc.campus);

    if( c != 0 )
    {
        //sort by campus if we can
        return c;
    }
    else
    {
        //campus equal, so sort by faculty
        return this.faculty.compareTo(gc.faculty);
    }
}

This is simplified but hopefully gives you an idea. Consult the Comparable and Comparator docs for more info.


If you know in advance which fields to use to make the comparison, then other people gave right answers.
What you may be interested in is to sort your collection in case you don't know at compile-time which criteria to apply. Imagine you have a program dealing with cities:



    protected Set<City> cities;
    (...)
    Field temperatureField = City.class.getDeclaredField("temperature");
    Field numberOfInhabitantsField = City.class.getDeclaredField("numberOfInhabitants");
    Field rainfallField = City.class.getDeclaredField("rainfall");
    program.showCitiesSortBy(temperatureField, numberOfInhabitantsField, rainfallField);
    (...)
    public void showCitiesSortBy(Field... fields) {
        List<City> sortedCities = new ArrayList<City>(cities);
        Collections.sort(sortedCities, new City.CityMultiComparator(fields));
        for (City city : sortedCities) {
            System.out.println(city.toString());
        }
    }

where you can replace hard-coded field names by field names deduced from a user request in your program.

In this example, City.CityMultiComparator<City> is a static nested class of class City implementing Comparator:



    public static class CityMultiComparator implements Comparator<City> {
        protected List<Field> fields;

        public CityMultiComparator(Field... orderedFields) {
            fields = new ArrayList<Field>();
            for (Field field : orderedFields) {
                fields.add(field);
            }
        }

        @Override
        public int compare(City cityA, City cityB) {
            Integer score = 0;
            Boolean continueComparison = true;
            Iterator itFields = fields.iterator();

            while (itFields.hasNext() && continueComparison) {
                Field field = itFields.next();
                Integer currentScore = 0;
                if (field.getName().equalsIgnoreCase("temperature")) {
                    currentScore = cityA.getTemperature().compareTo(cityB.getTemperature());
                } else if (field.getName().equalsIgnoreCase("numberOfInhabitants")) {
                    currentScore = cityA.getNumberOfInhabitants().compareTo(cityB.getNumberOfInhabitants());
                } else if (field.getName().equalsIgnoreCase("rainfall")) {
                    currentScore = cityA.getRainfall().compareTo(cityB.getRainfall());
                }
                if (currentScore != 0) {
                    continueComparison = false;
                }
                score = currentScore;
            }

            return score;
        }
    }


You may want to add an extra layer of precision, to specify, for each field, whether sorting should be ascendant or descendant. I guess a solution is to replace Field objects by objects of a class you could call SortedField, containing a Field object, plus another field meaning ascendant or descendant.


Hope this Helps:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

class Person implements Comparable {
  String firstName, lastName;

  public Person(String f, String l) {
    this.firstName = f;
    this.lastName = l;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String toString() {
    return "[ firstname=" + firstName + ",lastname=" + lastName + "]";
  }

  public int compareTo(Object obj) {
    Person emp = (Person) obj;
    int deptComp = firstName.compareTo(emp.getFirstName());

    return ((deptComp == 0) ? lastName.compareTo(emp.getLastName()) : deptComp);
  }

  public boolean equals(Object obj) {
    if (!(obj instanceof Person)) {
      return false;
    }
    Person emp = (Person) obj;
    return firstName.equals(emp.getFirstName()) && lastName.equals(emp.getLastName());
  }
}

class PersonComparator implements Comparator<Person> {
  public int compare(Person emp1, Person emp2) {
    int nameComp = emp1.getLastName().compareTo(emp2.getLastName());
    return ((nameComp == 0) ? emp1.getFirstName().compareTo(emp2.getFirstName()) : nameComp);
  }
}

public class Main {
  public static void main(String args[]) {
    ArrayList<Person> names = new ArrayList<Person>();
    names.add(new Person("E", "T"));
    names.add(new Person("A", "G"));
    names.add(new Person("B", "H"));
    names.add(new Person("C", "J"));

    Iterator iter1 = names.iterator();
    while (iter1.hasNext()) {
      System.out.println(iter1.next());
    }
    Collections.sort(names, new PersonComparator());
    Iterator iter2 = names.iterator();
    while (iter2.hasNext()) {
      System.out.println(iter2.next());
    }
  }
}

You just need to have your class inherit from Comparable.

then implement the compareTo method the way you like.


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to sorting

Sort Array of object by object field in Angular 6 Sorting a list with stream.sorted() in Java How to sort dates from Oldest to Newest in Excel? how to sort pandas dataframe from one column Reverse a comparator in Java 8 Find the unique values in a column and then sort them pandas groupby sort within groups pandas groupby sort descending order Efficiently sorting a numpy array in descending order? Swift: Sort array of objects alphabetically

Examples related to collections

Kotlin's List missing "add", "remove", Map missing "put", etc? How to unset (remove) a collection element after fetching it? How can I get a List from some class properties with Java 8 Stream? Java 8 stream map to list of keys sorted by values How to convert String into Hashmap in java How can I turn a List of Lists into a List in Java 8? MongoDB Show all contents from all collections Get nth character of a string in Swift programming language Java 8 Distinct by property Is there a typescript List<> and/or Map<> class/library?