How To Sort Objects In Java

It is almost impossible to imagine a Java developer who has not used or using Collection framework. In Collection framework, mostly used classes are ArrayList from List interface and HashSet, TreeSet from Set interface and we do often deal with sorting these Collections.

In this article, I will be mainly focusing on sorting ArrayList, HashSet, TreeSet from Collection and last but not the least the array.

Let's see how to sort a given collection of integers( 5,10,0,-1):

Data (Integers) stored in ArrayList.

private void sortNumbersInArrayList() {
        List<Integer> integers = new ArrayList<>();
        integers.add(5);
        integers.add(10);
        integers.add(0);
        integers.add(-1);
        System.out.println("Original list: " +integers);
        Collections.sort(integers);
        System.out.println("Sorted list: "+integers);
        Collections.sort(integers, Collections.reverseOrder());
        System.out.println("Reversed List: " +integers);
}

Output is:

Original list: [5, 10, 0, -1]
Sorted list: [-1, 0, 5, 10]
Reversed List: [10, 5, 0, -1]

Data (Integers) Stored in HashSet

private void sortNumbersInHashSet() {
        Set<Integer> integers = new HashSet<>();
        integers.add(5);
        integers.add(10);
        integers.add(0);
        integers.add(-1);
        System.out.println("Original set: " +integers);
       // Collections.sort(integers); This throws error since sort method accepts list not collection
        List list = new ArrayList(integers);
        Collections.sort(list);
        System.out.println("Sorted set: "+list);
        Collections.sort(list, Collections.reverseOrder());
        System.out.println("Reversed set: " +list);
 }

Output is:

Original set: [0, -1, 5, 10]
Sorted set: [-1, 0, 5, 10]
Reversed set: [10, 5, 0, -1]

In this example (Data(Integers) stored in HashSet), we saw that HashSet is converted to ArrayList to sort. Without converting to ArrayList, sorting can be achieved by using TreeSet. TreeSet is another implementation of Set and is sorted(ordered) using their natural ordering when the Set is created with the default constructor.

Data (Integers) Stored in TreeSet

 private void sortNumbersInTreeSet() {
        Set<Integer> integers = new TreeSet<>();
        integers.add(5);
        integers.add(10);
        integers.add(0);
        integers.add(-1);
        System.out.println("Original set: " + integers);
        System.out.println("Sorted set: "+ integers);
        Set<Integer> reversedIntegers = new TreeSet(Collections.reverseOrder());
        reversedIntegers.add(5);
        reversedIntegers.add(10);
        reversedIntegers.add(0);
        reversedIntegers.add(-1);
        System.out.println("Reversed set: " + reversedIntegers);
 }

Output is:

Original set: [-1, 0, 5, 10]
Sorted set: [-1, 0, 5, 10]
Reversed set: [10, 5, 0, -1]

In this case "Original set:" and "Sorted set:" both are same since we have used a TreeSet which stores data in sorted order, we didn't sort it after inserting.

So far everything seems to be working as expected and we are good at sorting. Now let's try to store a custom Object (say Student) in the various Collection and see how sorting works.

Data(Students) Stored in ArrayList

private void sortStudentInArrayList() {
        List<Student> students = new ArrayList<>();

        Student student1 = createStudent("Biplab", 3);
        students.add(student1);

        Student student2 = createStudent("John", 1);
        students.add(student2);

        Student student3 =  createStudent("Pal", 5);
        students.add(student3);

        Student student4 = createStudent("Biplab", 2);
        students.add(student4);

        System.out.println("Original students list: " + students);

        Collections.sort(students);// Error here
        System.out.println("Sorted students list: " + students);

        Collections.sort(students, Collections.reverseOrder());
        System.out.println("Reversed students list: " + students);
}
private Student createStudent(String name, int no) {
        Student student = new Student();
        student.setName(name);
        student.setNo(no);
        return student;
}

public class Student {
    String name;
    int no;

    public String getName() {
        return name;
    }

    public int getNo() {
        return no;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNo(int no) {
        this.no = no;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", no=" + no +
                '}';
    }
}

This throws compile time error with below error message :

sort(java.util.List<T>)
in Collections cannot be applied
to (java.util.List<com.example.demo.dto.Student>)
reason: no instance(s) of type variable(s) T exist so that Student 
conforms to Comparable<? Super T>

To solve this, either Student class needs to implement Comparable or a Comparator object needs to be passed while invoking Collections.sort. In case of Integer, sort method didn't complain since Integer class implements Comparable. Let's look, how implementing Comparable or passing a Comparator solves the issue and sort method sorts the Collection.

Sort using Comparable

package com.example.demo.dto;

public class Student implements Comparable{
    String name;
    int no;

    public String getName() {
        return name;
    }

    public int getNo() {
        return no;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNo(int no) {
        this.no = no;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", no=" + no +
                '}';
    }

    @Override
    public int compareTo(Object o) {
         return this.getName().compareTo(((Student) o).getName());
    }
}

Output is:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]
Reversed students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]

In all examples, to reverse we are using " Collections.sort(students, Collections.reverseOrder() ", instead it can be achieved by changing the implementation of compareTo(..)   method and the implementation of compareTo(...)   will look like :

 @Override
  public int compareTo(Object o) {
     return (((Student) o).getName()).compareTo(this.getName());
  }

Output is:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]
Reversed students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]

If we observe the output, we can see that "Sorted students list:" has students' information in reversed order (by name).

So far students sorting was done based on the Student's "name" not by "no". If we want to sort by "no", we just need to change the implementation of  compareTo(Object o)  of Student class and the implementation will look like:

 @Override
 public int compareTo(Object o) {
    return  (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1));
}

Output is:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='John', no=1}, Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='Pal', no=5}]
Reversed students list: [Student{name='Pal', no=5}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}]

In above output, we can see there are 2 students with "no" 2 and 3 have the same name as "Biplab".

Now let's say we need to sort these students first by "name" and if more than 1 students have the same name, those students need to be sorted by "no". To achieve this, we need to change the implementation of the compareTo(...)   method like :

  @Override
  public int compareTo(Object o) {
    int result = this.getName().compareTo(((Student) o).getName());
        if(result == 0) {
            result = (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1));
        }
        return  result;
 }

Output is:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}]
Reversed students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]

Sort Using Comparator

To sort Students by their "name", we will add a Comparator and pass it to the sort method:

public class Sorting {

 private void sortStudentInArrayList() {
        List<Student> students = new ArrayList<>();

        Student student1 = createStudent("Biplab", 3);
        students.add(student1);

        Student student2 = createStudent("John", 1);
        students.add(student2);

        Student student3 =  createStudent("Pal", 5);
        students.add(student3);

        Student student4 = createStudent("Biplab", 2);
        students.add(student4);


        System.out.println("Original students list: " + students);

        Collections.sort(integers, new NameComparator());
        System.out.println("Sorted students list: " + students);
 }
}
public class Student {
    String name;
    int no;

    public String getName() {
        return name;
    }

    public int getNo() {
        return no;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNo(int no) {
        this.no = no;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", no=" + no +
                '}';
    }
}

class NameComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());
    }
}

Output is :

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]

Similarly, if we want to sort students by their "no", one more comparator (NoComparator.java) can be added and passed it to sort method and data will get sorted by "no".

Now, if we want to sort students by "name" and later by "no", it can be achieved by combining both logic inside compare(...)   method.

class NameNoComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {

        int result = o1.getName().compareTo(o2.getName());
        if(result == 0) {
            result =  o1.getNo() <  o2.getNo() ? -1 : o1.getNo() == o2.getNo() ? 0 : 1;
        }
       return result;
    }
}

Output is:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}]

Data (Students) Stored in Set

In case of a  Set, we either need to convert the HashSet to ArrayList or use a TreeSet to sort the data. Also, we know to make Set work, equals(...)   and hashCode()   methods need to be overridden. Below is an example of overriding equals and hashcode based on "no" filed and these are IDE auto-generated codes. Other code related to Comparable or Comparator remains same as the ArrayList.

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return no == student.no;
    }

    @Override
    public int hashCode() {
        return Objects.hash(no);
    }

Data (Students) Stored in array

To sort an array, we need to do the same thing whatever we have done for sorting an ArrayList (either Implement Comparable or pass a comparator to sort method). In this case sort method is Arrays.sort(Object[] a )" notCollections.sort(..) ".

 private void sortStudentInArray() {
       Student [] students = new Student[4];

        Student student1 = createStudent("Biplab", 3);
        students[0] = student1;

        Student student2 = createStudent("John", 1);
        students[1] = student2;

        Student student3 =  createStudent("Pal", 5);
        students[2] = student3;

        Student student4 = createStudent("Biplab", 2);
        students[3] = student4;


        System.out.print("Original students list: ");
        for (Student student: students) {
            System.out.print( student + " ,");

        }

        Arrays.sort(students);

        System.out.print("\nSorted students list: ");
        for (Student student: students) {
            System.out.print( student +" ,");
        }

        Arrays.sort(students, Collections.reverseOrder());
        System.out.print("\nReversed students list: " );
        for (Student student: students) {
            System.out.print( student +" ,");
        }

   }

//Student class
// All properties goes here
  @Override
   public int compareTo(Object o) {
       int result =this.getName().compareTo(((Student)o).getName());
       if(result ==0) {
           result =  (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1));
       }
       return  result;
   }

Output is :

Original students list: Student{name='Biplab', no=3} ,Student{name='John', no=1} ,Student{name='Pal', no=5} ,Student{name='Biplab', no=2} ,
Sorted students list: Student{name='Biplab', no=2} ,Student{name='Biplab', no=3} ,Student{name='John', no=1} ,Student{name='Pal', no=5} ,
Reversed students list: Student{name='Pal', no=5} ,Student{name='John', no=1} ,Student{name='Biplab', no=3} ,Student{name='Biplab', no=2} ,


Conclusion :

We often get confused regarding the usage of Comparable/Comparator and when to use what. In my opinion, below are the scenario when we should be using them.

Comparator:

Comparable :

Note: I have not covered details about Set/List, how they work etc. The assumption here is that reader knows about these. Also, details example for sorting using Set/array is not provided reason being implementations are very similar to ArrayList for which I have given example in detail. If somebody needs detail examples, please feel free to add a comment and I will be more than happy to provide an example in details.

Finally, thanks for reading.

 

 

 

 

Top