Sorting Java ArrayList

Sorting Java ArrayList

3 Comments

ArrayList is one of the most commonly used collection classes of the Java Collection Framework because of the functionality and flexibility it provides. ArrayList is a List implementation that internally implements a dynamic array to store elements. Therefore, an ArrayList can dynamically grow and shrink as you add and remove elements to and from it. It is likely that you have used ArrayList, therefore I will skip the basics. If you are not familiar with ArrayList yet, you can go through its API documentation here, which is very descriptive and easy to understand to perform basic operations on ArrayList.

In this post, I will discuss one of the most important operation on ArrayList that you will most likely require implementing during enterprise application development. It’s sorting the elements of an ArrayList.

Sorting an ArrayList of String Objects

Consider an ArrayList that stores country names as String objects. To sort the ArrayList, you need to simply call the Collections.sort() method passing the ArrayList object populated with country names. This method will sort the elements (country names) of the ArrayList using natural ordering (alphabetically in ascending order). Lets’s write some code for it.

SortArrayListAscendingDescending.java

package guru.springframework.blog.sortarraylist.ascendingdescending;

import java.util.ArrayList;
import java.util.Collections;

public class SortArrayListAscendingDescending {
    private ArrayList arrayList;

    public SortArrayListAscendingDescending(ArrayList arrayList) {
        this.arrayList = arrayList;
    }

    public ArrayList getArrayList() {
        return this.arrayList;
    }

    public ArrayList sortAscending() {
        Collections.sort(this.arrayList);
        return this.arrayList;
    }

    public ArrayList sortDescending() {
        Collections.sort(this.arrayList, Collections.reverseOrder());
        return this.arrayList;
    }
}

In the class above, we initialized an ArrayList object in the constructor. In the sortAscending() method, we called the Collections.sort() method passing the initialized ArrayList object and returned the sorted ArrayList. In the sortDescending() method we called the overloaded Collections.sort() method to sort the elements in descending order. This version of Collections.sort() accepts the ArrayList object as the first parameter and a Comparator object that the Collections.reverseOrder() method returns as the second parameter. We will come to Comparator a bit later. To test the sorting functionality, we will write some test code.

SortArrayListAscendingDescendingTest.java

package guru.springframework.blog.sortarraylist.ascendingdescending;

import org.junit.Test;

import java.util.ArrayList;

import static org.junit.Assert.*;


public class SortArrayListAscendingDescendingTest {

    @Test
    public void testSortAscendingDescending() throws Exception {
        ArrayList countryList = new ArrayList<>();
        countryList.add("France");
        countryList.add("USA");
        countryList.add("India");
        countryList.add("Spain");
        countryList.add("England");
        SortArrayListAscendingDescending sortArrayList = new SortArrayListAscendingDescending(countryList);
        ArrayList unsortedArrayList = sortArrayList.getArrayList();
        System.out.println("Unsorted ArrayList: " + unsortedArrayList);
        ArrayList sortedArrayListAscending = sortArrayList.sortAscending();
        System.out.println("Sorted ArrayList in Ascending Order : " + sortedArrayListAscending);
        ArrayList sortedArrayListDescending = sortArrayList.sortDescending();
        System.out.println("Sorted ArrayList in Descending Order: " + sortedArrayListDescending);
    }
}

In the test code above, we created a ArrayList object and added five String objects that represent the names of five countries to it. We then called the getArrayList(), sortAscending(), and sortDescending() methods and printed out the ArrayList objects that the methods return.

The output is this.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

Running guru.springframework.blog.sortarraylist.ascendingdescending.SortArrayListAscendingDescendingTest

Unsorted ArrayList: [France, USA, India, Spain, England]
Sorted ArrayList in Ascending Order : [England, France, India, Spain, USA]
Sorted ArrayList in Descending Order: [USA, Spain, India, France, England]

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec - in guru.springframework.blog.sortarraylist.ascendingdescending.SortArrayListAscendingDescendingTest

At this point, it might appear that sorting elements of an ArrayList is very simple. We only need to call the Collections.sort() method passing the ArrayList object whose elements needs to be sorted. But, there is more to sorting ArrayLists as you encounter additional scenarios.

The Collections.sort() method sorts ArrayList elements or elements of any other List implementation provided the elements are comparable. What this means programmatically is that the classes of the elements need to implement the Comparable interface of the java.lang package. As the String class implements the Comparable interface, we were able to sort the ArrayList of country names. Some other classes standard to Java which implement the Comparable interface include the primitive wrapper classes, such as Integer, Short, Double, Float, and Boolean. BigInteger, BigDecimal, File, and Date are also examples of classes that implement Comparable.

Sorting an ArrayList using Comparable

Comparable is an interface with a single compareTo() method. An object of a class implementing Comparable is capable of comparing itself with another object of the same type. The class implementing Comparable needs to override the compareTo() method. This method accepts an object of the same type and implements the logic for comparing this object with the one passed to compareTo(). The compareTo() method returns the comparison result as an integer that has the following meanings:

  • A positive value indicates that this object is greater than the object passed to compareTo().
  • A negative value indicates that this object is less than the object passed to compareTo().
  • The value zero indicates that both the objects are equal.

Let’s take an example of a JobCandidate class whose objects we want to store in a ArrayList and later sort them. The JobCandidate class has three fields: name and gender of type String and age that is an integer. We want to sort JobCandidate objects stored in the ArrayList based on the age field. To do so, we will need to write the JobCandidate class to implement Comparable and override the compareTo() method.

The code of the JobCandidate class is this.

JobCandidate.java

package guru.springframework.blog.sortarraylist.comparable;


public class JobCandidate implements Comparable {
    private String name;
    private String gender;
    private int age;

    public JobCandidate(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int compareTo(JobCandidate candidate) {
         return (this.getAge() < candidate.getAge() ? -1 :
                (this.getAge() == candidate.getAge() ? 0 : 1));
    }

    @Override
    public String toString() {
        return " Name: " + this.name + ", Gender: " + this.gender + ", age:" + this.age;
    }
}

In the overridden compareTo() method of the JobCandidate class above, we implemented the comparison logic based on the age field. I have seen many programmers restoring to the shortcut version of returning the comparison result as return (this.getAge() - candidate.getAge());. Although using this return statement might appear tempting and will not anyhow affect our example, my advice is to stay away from it. Imagine, the result of comparing integer values where one or both of them are negative values. It can lead to bugs that will make your application behave erratically and more than that, such bugs being subtle, are extremely difficult to detect especially in large enterprise applications. Next, we’ll write a helper class which will sort ArrayList objects containing JobCandidate elements for clients.

JobCandidateSorter.java

    package guru.springframework.blog.sortarraylist.comparable;


import java.util.ArrayList;
import java.util.Collections;

public class JobCandidateSorter {
    ArrayList jobCandidate = new ArrayList<>();

    public JobCandidateSorter(ArrayList jobCandidate) {
        this.jobCandidate = jobCandidate;
    }

    public ArrayList getSortedJobCandidateByAge() {
        Collections.sort(jobCandidate);
        return jobCandidate;
    }
}

In the JobCandidateSorter class we initialized a ArrayList object that client will pass through the constructor while instantiating JobCandidateSorter. We then wrote the getSortedJobCandidateByAge() method. In this method, we called Collections.sort() passing the initialized ArrayList. Finally, we returned back the sorted ArrayList.

Next, we will write a test class to test our code.

JobCandidateSorterTest.java

package guru.springframework.blog.sortarraylist.comparable;

import org.junit.Test;

import java.lang.reflect.Array;
import java.util.ArrayList;

import static org.junit.Assert.*;


public class JobCandidateSorterTest {

    @Test
    public void testGetSortedJobCandidateByAge() throws Exception {
        JobCandidate jobCandidate1 = new JobCandidate("Mark Smith", "Male", 26);
        JobCandidate jobCandidate2 = new JobCandidate("Sandy Hunt", "Female", 23);
        JobCandidate jobCandidate3 = new JobCandidate("Betty Clark", "Female", 20);
        JobCandidate jobCandidate4 = new JobCandidate("Andrew Styne", "Male", 24);
        ArrayList jobCandidateList = new ArrayList<>();
        jobCandidateList.add(jobCandidate1);
        jobCandidateList.add(jobCandidate2);
        jobCandidateList.add(jobCandidate3);
        jobCandidateList.add(jobCandidate4);
        JobCandidateSorter jobCandidateSorter = new JobCandidateSorter(jobCandidateList);
        ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByAge();
        System.out.println("-----Sorted JobCandidate by age: Ascending-----");
        for (JobCandidate jobCandidate : sortedJobCandidate) {
            System.out.println(jobCandidate);
        }

    }
}

In the test class above, we created four JobCandidate objects and added them to an ArrayList. We then instantiated the JobCandidateSorter class passing our ArrayList to its constructor. Finally, we called the getSortedJobCandidateByAge() method of JobCandidateSorter and printed out the sorted ArrayList that the method returns. The output on running the test is this.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running guru.springframework.blog.sortarraylist.comparable.JobCandidateSorterTest
-----Sorted JobCandidate by age: Ascending-----
 Name: Betty Clark, Gender: Female, age:20
 Name: Sandy Hunt, Gender: Female, age:23
 Name: Andrew Styne, Gender: Male, age:24
 Name: Mark Smith, Gender: Male, age:26
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.003 sec - in guru.springframework.blog.sortarraylist.comparable.JobCandidateSorterTest

Sorting ArrayList using Comparable is a common approach. But, you need to be aware of certain constraints. The class whose object you want to sort must implement Comparable and override the compareTo() method. This essentially means you would only be able to compare the objects based on one field (which was age in our example). What if the requirements state you need to be able to sort JobCandidate objects by name and also by age? Comparable is not the solution. In addition, comparison logic is part of the class whose objects needs to be compared, which eliminates any chance of reusability of the comparison logic. Java addresses such comparison requirements used in sorting by providing the Comparator interface in the java.util package.

Sorting an ArrayList using Comparator

The Comparator interface similar to the Comparable interface provides a single comparison method named compare(). However, unlike the compareTo() method of Comparable, the compare() method takes two different objects of the same type for comparison.
We will use Comparator to sort objects of the same JobCandidate class we used earlier but with few differences. We will allow sorting JobCandidate objects both by name and age by implementing Comparator as anonymous inner classes.

Here is the code of the JobCandidate class using Comparator.

JobCandidate.java

package guru.springframework.blog.sortarraylist.comparator;


import java.util.Comparator;

public class JobCandidate {
    private String name;
    private String gender;
    private int age;

    public JobCandidate(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public int getAge() {
        return age;
    }

    public static Comparator ageComparator = new Comparator() {
        @Override
        public int compare(JobCandidate jc1, JobCandidate jc2) {
            return (jc2.getAge() < jc1.getAge() ? -1 :
                    (jc2.getAge() == jc1.getAge() ? 0 : 1));
          }
    };

    public static Comparator nameComparator = new Comparator() {
        @Override
        public int compare(JobCandidate jc1, JobCandidate jc2) {
            return (int) (jc1.getName().compareTo(jc2.getName()));
        }
    };


    @Override
    public String toString() {
        return " Name: " + this.name + ", Gender: " + this.gender + ", age:" + this.age;
    }
}

In the class above, from Line 29 – Line 35, we wrote an anonymous class and implemented the compare() method that will allow sorting JobCandidate objects by age in descending order. From Line 37 – Line 42, we again wrote an anonymous class and implemented the compare() method that will allow sorting JobCandidate objects by name in ascending order. We will now write a class that will sort the elements of the ArrayList for clients.

JobCandidateSorter.java

package guru.springframework.blog.sortarraylist.comparator;


import java.util.ArrayList;
import java.util.Collections;

public class JobCandidateSorter {
    ArrayList jobCandidate = new ArrayList<>();

    public JobCandidateSorter(ArrayList jobCandidate) {
        this.jobCandidate = jobCandidate;
    }

    public ArrayList getSortedJobCandidateByAge() {
        Collections.sort(jobCandidate, JobCandidate.ageComparator);
        return jobCandidate;
    }

    public ArrayList getSortedJobCandidateByName() {
        Collections.sort(jobCandidate, JobCandidate.nameComparator);
        return jobCandidate;
    }
}

In the class above, we wrote the getSortedJobCandidateByAge() method. In this method we called the overloaded version of Collections.sort() passing the ArrayList object to be sorted and the Comparator object that compares age. In the getSortedJobCandidateByName() method, we again called the overloaded version of Collections.sort() passing the ArrayList object to be sorted and the Comparator object to compare names.

Let’s write a test class to test our code.

JobCandidateSorterTest.java

package guru.springframework.blog.sortarraylist.comparator;


import guru.springframework.blog.sortarraylist.comparator.JobCandidate;
import guru.springframework.blog.sortarraylist.comparator.JobCandidateSorter;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;

import static org.junit.Assert.*;

public class JobCandidateSorterTest {
    JobCandidateSorter jobCandidateSorter;

    @Before
    public void setUp() throws Exception {
        JobCandidate jobCandidate1 = new JobCandidate("Mark Smith", "Male", 26);
        JobCandidate jobCandidate2 = new JobCandidate("Sandy Hunt", "Female", 23);
        JobCandidate jobCandidate3 = new JobCandidate("Betty Clark", "Female", 20);
        JobCandidate jobCandidate4 = new JobCandidate("Andrew Styne", "Male", 24);
        ArrayList jobCandidateList = new ArrayList<>();
        jobCandidateList.add(jobCandidate1);
        jobCandidateList.add(jobCandidate2);
        jobCandidateList.add(jobCandidate3);
        jobCandidateList.add(jobCandidate4);
        jobCandidateSorter = new JobCandidateSorter(jobCandidateList);
    }

    @Test
    public void testGetSortedJobCandidateByAge() throws Exception {
        System.out.println("-----Sorted JobCandidate by age: Descending-----");
        ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByAge();
        for (JobCandidate jobCandidate : sortedJobCandidate) {
            System.out.println(jobCandidate);
        }
    }

    @Test
    public void testGetSortedJobCandidateByName() throws Exception {
        System.out.println("-----Sorted JobCandidate by name: Ascending-----");
        ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByName();
        for (JobCandidate jobCandidate : sortedJobCandidate) {
            System.out.println(jobCandidate);
        }

    }
}

In the test class we populated JobCandidate objects in an ArrayList and created a JobCandidateSorter object in the JUnit setup() method annotated with @Before. If you are new to JUnit, you can refer my post covering JUnit annotations (Part of a series on unit testing with JUnit) here. In the testGetSortedJobCandidateByAge() test method we called the getSortedJobCandidateByAge() method and printed out the sorted ArrayList that the method returns. In the testGetSortedJobCandidateByName() test method, we called the getSortedJobCandidateByName() method and printed out the sorted ArrayList that the method returns. The output of the test is this.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

Running guru.springframework.blog.sortarraylist.comparator.JobCandidateSorterTest
-----Sorted JobCandidate by name: Ascending-----
 Name: Andrew Styne, Gender: Male, age:24
 Name: Betty Clark, Gender: Female, age:20
 Name: Mark Smith, Gender: Male, age:26
 Name: Sandy Hunt, Gender: Female, age:23
-----Sorted JobCandidate by age: Descending-----
 Name: Mark Smith, Gender: Male, age:26
 Name: Andrew Styne, Gender: Male, age:24
 Name: Sandy Hunt, Gender: Female, age:23
 Name: Betty Clark, Gender: Female, age:20
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 sec - in guru.springframework.blog.sortarraylist.comparator.JobCandidateSorterTest

Conclusion

In this post we looked at different approaches of sorting elements of ArrayList. One using Comparable and the other using Comparator. The approach to choose has always been a cause of confusion for programmers. What you should essentially remember is that a Comparable object can say “I can compare myself with another object” while a Comparator object can say “I can compare two different objects”. You cannot say that one interface is better than the other. The interface you choose depends upon the functionality you need to achieve.

About jt

    You May Also Like

    3 comments on “Sorting Java ArrayList

    1. November 4, 2015 at 5:17 am

      Very good explanation about sorting arrayList.
      Thank you John sharing your experience with us.

      Reply
      • November 4, 2015 at 7:52 am

        Thanks!!

        Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.