16

I have a list of objects. Each object contains a String and a Date (amongst others).

I want to first sort by the String and then by the Date.

How could this be done in the cleanest way possible?

Thanks!

Krt_Malta

Krt_Malta
  • 9,265
  • 18
  • 53
  • 91

9 Answers9

33

With Java 8, this is really easy. Given

class MyClass {
    String getString() { ... }
    Date getDate() { ... }
}

You can easily sort a list as follows:

List<MyClass> list = ...
list.sort(Comparator.comparing(MyClass::getString).thenComparing(MyClass::getDate));
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
23

Given an object class that looks like this:

public class MyObject {
    public String getString() { ... }
    public Date getDate() { ... }
    ...
}

Write a custom comparator class like so:

public class ObjectComparator implements Comparator{

    public int compare(Object obj1, Object obj2) {
        MyObject myObj1 = (MyObject)obj1;
        MyObject myObj2 = (MyObject)obj2;
        stringResult = myObj1.getString().compareTo(myObj2.getString());
        if (stringResult == 0) {
            // Strings are equal, sort by date
            return myObj1.getDate().compareTo(myObj2.getDate());
        }
        else {
            return stringResult;
        }
    }
}

Then sort as follows:

Collections.sort(objectList, new ObjectComparator());
unnamedwill
  • 246
  • 1
  • 2
  • 2
    @Krt_Malta the most clear one? I don't think so. For one thing, it uses the pre-1.5 non-generic version of comparator, which is a lot more verbose and error-prone. I82Much's answer is much better, for example. – Sean Patrick Floyd Apr 08 '11 at 14:03
  • I'll actually agree myself I should have done the generic version of comparator, and Guava comparison chain sounds like something worth looking into if you're happy to pull that dependency into your project. (Presumably it has lots of other great stuff in it as well.) – unnamedwill Apr 13 '11 at 10:41
  • @unnamedwill out of curiosity, if you agree it's better to use Comparator, why not edit your answer? At this time I82Much's answer is 3rd in votes, so some very hasty people might not read it or these comments and just implement your answer without generics. – Blueriver Oct 14 '16 at 14:51
8

Implement a custom Comparator, using a compare(a,b) method like the following:

Plain Java:

 public int compare(YourObject o1, YourObject o2) {
    int result = o1.getProperty1().compareTo(o2.getProperty1()));
    if(result==0) result = o1.getProperty2().compareTo(o2.getProperty2());
    return result;
 }

With Guava (using ComparisonChain):

public int compare(YourObject o1, YourObject o2) {
    return ComparisonChain.start()
      .compare(o1.getProperty1(), o2.getProperty1())
      .compare(o1.getProperty2(), o2.getProperty2())
      .result();
 }

With Commons / Lang (using CompareToBuilder):

public int compare(YourObject o1, YourObject o2) {
    return new CompareToBuilder()
      .append(o1.getProperty1(), o2.getProperty1())
      .append(o1.getProperty2(), o2.getProperty2())
      .toComparison();
 }

(All three versions are equivalent, but the plain Java version is the most verbose and hence most error-prone one. All three solutions assume that both o1.getProperty1() and o1.getProperty2() implement Comparable).

(Taken from this previous answer of mine)


now do Collections.sort(yourList, yourComparator)

Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
7

The Comparators answer is correct but incomplete.

StringAndDateComparator implements Comparator<MyObject> {

   public int compare(MyObject first, MyObject second) {
        int result = first.getString().compareTo(second.getString());
        if (result != 0) {
            return result;
        }
        else {
            return first.getDate().compareTo(second.getDate());
        }
}

GlazedLists has a nice utility method to chain together different comparators to save you from writing this boilerplate. See the chainComparators method for more information.

Krt_Malta
  • 9,265
  • 18
  • 53
  • 91
I82Much
  • 26,901
  • 13
  • 88
  • 119
4

A simple array can be sorted using 2 lambda experessions as:

Arrays.sort(arr, (i, j) -> (i[0] == j[0] ? j[1] - i[1] : i[0] - j[0]));

means two subarrays i & j within a 2D array arr will be sorted in ascending order based on 0th index of arrays. And if 0th index is equal, then based on 1st index.

Shubham Uniyal
  • 111
  • 1
  • 9
2
 package core.java.collection;

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

public class GroupByComparator {

public static void main(String[] args) {

List <StudentTest>  studList = new ArrayList<StudentTest>(); 

    StudentTest s1 = new StudentTest(12 ,"Devendra" ,410);
    StudentTest s2 = new StudentTest(11 ,"Devendra" ,430);
    StudentTest s3 = new StudentTest(13 ,"Devendra" ,402);
    StudentTest s4 = new StudentTest(10 ,"Devendra" ,432);
    //Assuming that id may be same
    StudentTest s5 = new StudentTest(14 ,"Singraul" ,432);
    StudentTest s6 = new StudentTest(14 ,"Abhishek" ,432);
    StudentTest s7 = new StudentTest(14 ,"Roshan" ,432);
    StudentTest s8 = new StudentTest(14 ,"Bikas" ,432);

    StudentTest s9 = new StudentTest(15 ,"Devlal" ,450);
    StudentTest s10 = new StudentTest(15 ,"Devlal" ,359);
    StudentTest s11= new StudentTest(15 ,"Devlal" ,430);
    StudentTest s12 = new StudentTest(15 ,"Devlal" ,420);

    studList.add(s1); studList.add(s2); studList.add(s3); studList.add(s4); studList.add(s5);
    studList.add(s6); studList.add(s7); studList.add(s8); studList.add(s9); studList.add(s10);
    studList.add(s11); studList.add(s12);

    Collections.sort(studList, new StudentComparator());
    // group by sorting
    System.out.println(studList);

}

}

 // Group by Comparator for ascending order
 class StudentComparator implements Comparator<StudentTest>{

@Override
public int compare(StudentTest newObj, StudentTest oldObj) {
    int result =0;
    // sort by name  first 
    result=  newObj.getStudName().compareTo(oldObj.getStudName());
    // sort by student id  second
    if(result == 0) {
        result=  newObj.getStudId()-oldObj.getStudId() ; // negative means before
    }
     // sort by marks third
    if(result == 0) {
        result=   Float.compare(newObj.getMarks(), oldObj.getMarks()); ; // negative means before
    }

    return result;
}

  }

class StudentTest{

private int studId ;
private String studName ;
private float marks ;

public StudentTest(int studId, String studName, float marks) {
    super();
    this.studId = studId;
    this.studName = studName;
    this.marks = marks;
}

public int getStudId() {
    return studId;
}

public void setStudId(int studId) {
    this.studId = studId;
}

public String getStudName() {
    return studName;
}

public void setStudName(String studName) {
    this.studName = studName;
}

public float getMarks() {
    return marks;
}

public void setMarks(float marks) {
    this.marks = marks;
}

@Override
public String toString() {
    return "StudentTest [studId=" + studId + ", studName=" + studName + ", marks=" + marks + "]";
}

}

1

Using java 8 and parallel sorting technique, we can also achieve this as follows:

List<Employee> empss  = getEmployees();
Comparator<Employee> combinedComparator = Comparator.comparing(Employee::getFName)
                                                    .thenComparing(Employee::getLName);
Employee[] emppArr = employees.toArray(new Employee[empss.size()]);

//Parallel sorting
Arrays.parallelSort(emppArr, combinedComparator);
KayV
  • 12,987
  • 11
  • 98
  • 148
1

Try this method:

Collections.sort(list, comparator)

You should of course have a custom Comparator implementation for your object, as stated by Manoj.

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
Guillaume
  • 5,535
  • 1
  • 24
  • 30
0

Try this way....

studentlist.stream().sorted(Comparator.comparing(Student::getAge).thenComparing(Student::getName)).forEach(System.out::println);
Govind Singh
  • 15,282
  • 14
  • 72
  • 106