Sorting Java ArrayList
3 CommentsArrayList
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.
MadhavaRao
Very good explanation about sorting arrayList.
Thank you John sharing your experience with us.
jt
Thanks!!