Java sorting multiple fields

Sort a List of objects by multiple fields [duplicate]

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?

@Moonbeam, the text of my question showed that I researched Collections and sorting, and I showed that I had already read other similar questions here on Stackoverflow. What makes you think I’m just fishing for code? Next time, please don’t disregard Wheaton’s Law.

@Moonbeam, sometimes you see to see code to understand a concept. Sure something like «My treeview flickers something awful!» «Try this» «Thanks!» doesnt help anyone learn, but that’s why this is stackOverflow and not some forum. See me after class.

6 Answers 6

Your Comparator would look like this:

public class GraduationCeremonyComparator implements Comparator  < public int compare(GraduationCeremony o1, GraduationCeremony o2) < int value1 = o1.campus.compareTo(o2.campus); if (value1 == 0) < int value2 = o1.faculty.compareTo(o2.faculty); if (value2 == 0) < return o1.building.compareTo(o2.building); >else < return value2; >> return value1; > > 

Basically it continues comparing each successive attribute of your class whenever the compared attributes so far are equal ( == 0 ).

Читайте также:  Html css отступы и границы

Thanks. Your explanation helped the penny to drop. I have a clearer understanding of the usage of the compare() method now that I didn’t have before.

don’t forget your null checks. The line ‘int value1 = o1.campus.compareTo(o2.campus);’ will throw a NullPointerException if o1 is null

Yes, you absolutely can do this. For example:

public class PersonComparator implements Comparator  < 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.»

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 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) < ListsortedCities = new ArrayList(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 is a static nested class of class City implementing Comparator :

 public static class CityMultiComparator implements Comparator  < protected Listfields; public CityMultiComparator(Field. orderedFields) < fields = new ArrayList(); 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.

Источник

Sorting a Stream by Multiple Fields in Java

Learn to sort the streams of objects by multiple fields using Comparators and Comparator.thenComparing() method. This method returns a lexicographic-order comparator with another comparator. It gives the same effect as SQL GROUP BY clause.

1. Creating Comparators for Multiple Fields

To sort on multiple fields, we must first create simple comparators for each field on which we want to sort the stream items. Then we chain these Comparator instances in the desired order to give GROUP BY effect on complete sorting behavior.

Note that Comparator provides a few other methods that we can use if they fit in the requirements.

  • thenComparing(keyExtractor) :
  • thenComparing(comparator)
  • thenComparing(keyExtractor, comparator)
  • thenComparingDouble(keyExtractor)
  • thenComparingInt(keyExtractor)
  • thenComparingLong(keyExtractor)
//first name comparator Comparator compareByFirstName = Comparator.comparing( Employee::getFirstName ); //last name comparator Comparator compareByLastName = Comparator.comparing( Employee::getLastName ); //Compare by first name and then last name (multiple fields) Comparator compareByFullName = compareByFirstName.thenComparing(compareByLastName); //Using Comparator - pseudo code list.stream().sorted( comparator ).collect();

2. Sorting with Complex Comparator

Given below is an example of using thenComparing() to create Comparator which is capable of sorting the employees’ list by their first name and last name.

import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class Main < public static void main(String[] args) < ArrayListemployees = getUnsortedEmployeeList(); //Compare by first name and then last name Comparator compareByName = Comparator .comparing(Employee::getFirstName) .thenComparing(Employee::getLastName); List sortedEmployees = employees.stream() .sorted(compareByName) .collect(Collectors.toList()); System.out.println(sortedEmployees); > private static ArrayList getUnsortedEmployeeList() < ArrayListlist = new ArrayList<>(); list.add( new Employee(2l, "Lokesh", "Gupta") ); list.add( new Employee(1l, "Alex", "Gussin") ); list.add( new Employee(4l, "Brian", "Sux") ); list.add( new Employee(5l, "Neon", "Piper") ); list.add( new Employee(3l, "David", "Beckham") ); list.add( new Employee(7l, "Alex", "Beckham") ); list.add( new Employee(6l, "Brian", "Suxena") ); return list; > >
[E[id=7, firstName=Alex, lastName=Beckham], E [id=1, firstName=Alex, lastName=Gussin], E [id=4, firstName=Brian, lastName=Sux], E [id=6, firstName=Brian, lastName=Suxena], E [id=3, firstName=David, lastName=Beckham], E [id=2, firstName=Lokesh, lastName=Gupta], E [id=5, firstName=Neon, lastName=Piper]]

Similar to the chained predicates, we can combine any number of Comparators to create any complex sorting logic and sort the Stream items with it.

We can use other Comparator methods as well as documented in the official Java docs.

Источник

How to sort by two fields in Java?

I have array of objects person (int age; String name;) . How can I sort this array alphabetically by name and then by age? Which algorithm would you use for this ?

15 Answers 15

You can use Collections.sort as follows:

private static void order(List persons) < Collections.sort(persons, new Comparator() < public int compare(Object o1, Object o2) < String x1 = ((Person) o1).getName(); String x2 = ((Person) o2).getName(); int sComp = x1.compareTo(x2); if (sComp != 0) < return sComp; >Integer x1 = ((Person) o1).getAge(); Integer x2 = ((Person) o2).getAge(); return x1.compareTo(x2); >>); > 

List is now sorted by name, then by age.

String.compareTo «Compares two strings lexicographically» — from the docs.

Collections.sort is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare method.

Since the OP already has their own object class, it would make more sense to implement Comparable . See the answer by @berry120

Mini code review: the else clause is redundant because the first return acts as a guard clause. Great answer though, worked a treat for me.

As this question/ answer still gets linked, please note that with Java SE 8 this became much simpler. If there are getters you can write Comparator comparator = Comparator.comparing(Person::getName).thenComparingInt(Person::getAge);

For those able to use the Java 8 streaming API, there is a neater approach that is well documented here: Lambdas and sorting

I was looking for the equivalent of the C# LINQ:

I found the mechanism in Java 8 on the Comparator:

So here is the snippet that demonstrates the algorithm.

 Comparator comparator = Comparator.comparing(person -> person.name); comparator = comparator.thenComparing(Comparator.comparing(person -> person.age)); 

Check out the link above for a neater way and an explanation about how Java’s type inference makes it a bit more clunky to define compared to LINQ.

Here is the full unit test for reference:

@Test public void testChainedSorting() < // Create the collection of people: ArrayListpeople = new ArrayList<>(); people.add(new Person("Dan", 4)); people.add(new Person("Andi", 2)); people.add(new Person("Bob", 42)); people.add(new Person("Debby", 3)); people.add(new Person("Bob", 72)); people.add(new Person("Barry", 20)); people.add(new Person("Cathy", 40)); people.add(new Person("Bob", 40)); people.add(new Person("Barry", 50)); // Define chained comparators: // Great article explaining this and how to make it even neater: // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/ Comparator comparator = Comparator.comparing(person -> person.name); comparator = comparator.thenComparing(Comparator.comparing(person -> person.age)); // Sort the stream: Stream personStream = people.stream().sorted(comparator); // Make sure that the output is as expected: List sortedPeople = personStream.collect(Collectors.toList()); Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age); Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age); Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age); Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age); Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age); Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age); Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age); Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age); Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age); // Andi : 2 // Barry : 20 // Barry : 50 // Bob : 40 // Bob : 42 // Bob : 72 // Cathy : 40 // Dan : 4 // Debby : 3 > /** * A person in our system. */ public static class Person < /** * Creates a new person. * @param name The name of the person. * @param age The age of the person. */ public Person(String name, int age) < this.age = age; this.name = name; >/** * The name of the person. */ public String name; /** * The age of the person. */ public int age; @Override public String toString() < if (name == null) return super.toString(); else return String.format("%s : %d", this.name, this.age); >> 

Источник

Оцените статью