Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Different Approaches To Sorting Elements of an ArrayList in Java

DZone's Guide to

Different Approaches To Sorting Elements of an ArrayList in Java

Learn different approaches for sorting elements of an ArrayList. One using Comparable and the other using Comparator.

· Java Zone
Free Resource

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

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<String> arrayList;       

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

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

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

  public ArrayList<String> 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 theCollections.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<String> countryList = new ArrayList<>();         
    countryList.add("France");         
    countryList.add("USA");         
    countryList.add("India");         
    countryList.add("Spain");         
    countryList.add("England");         
    SortArrayListAscendingDescending sortArrayList = new SortArrayListAscendingDescending(countryList);         
    ArrayList<String> unsortedArrayList = sortArrayList.getArrayList();         
    System.out.println("Unsorted ArrayList: " + unsortedArrayList);         
    ArrayList<String> sortedArrayListAscending = sortArrayList.sortAscending();         
    System.out.println("Sorted ArrayList in Ascending Order : " + sortedArrayListAscending);         
    ArrayList<String> 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 theCollections.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 theComparable 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 IntegerShort,DoubleFloat, and BooleanBigIntegerBigDecimalFile, 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 Comparableis 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<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;     
  }       

  @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> jobCandidate = new ArrayList<>();       

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

  public ArrayList<JobCandidate> 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<JobCandidate> jobCandidateList = new ArrayList<>();         
    jobCandidateList.add(jobCandidate1);         
    jobCandidateList.add(jobCandidate2);         
    jobCandidateList.add(jobCandidate3);         
    jobCandidateList.add(jobCandidate4);         
    JobCandidateSorter jobCandidateSorter = new JobCandidateSorter(jobCandidateList);         
    ArrayList<JobCandidate> 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 ArrayListthat 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 namedcompare(). 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 Comparatoras 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<JobCandidate> ageComparator = new Comparator<JobCandidate>() {         
    @Override         
    public int compare(JobCandidate jc1, JobCandidate jc2) {             
      return (jc2.getAge() < jc1.getAge() ? -1 :                     
              (jc2.getAge() == jc1.getAge() ? 0 : 1));           
    }     
  };       

  public static Comparator<JobCandidate> nameComparator = new Comparator<JobCandidate>() {         
    @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 sortingJobCandidate objects by name in ascending order. We will now write a class that will sort the elements of theArrayList for clients.

JobCandidateSorter.java

package guru.springframework.blog.sortarraylist.comparator;     

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

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

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

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

  public ArrayList<JobCandidate> 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 Comparatorobject 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 Comparatorobject 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<JobCandidate> 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<JobCandidate> 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<JobCandidate> 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.


Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:
java ,sorting

Published at DZone with permission of John Thompson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}