<

Java Collections - Comparable Vs Comparator

Published on
13,636 Points
7,236 Views
4 Endorsements
Last Modified:
Approved
Java contains several comparison operators (e.g., <, <=, >, >=, ==, !=) that allow you to compare primitive values. However, these operators cannot be used to compare the contents of objects.

Interface Comparable is used to allow objects of a class that implements the interface to be compared to one another. The interface contains one method, compareTo, that compares the object that calls the method to the object passed as an argument to the method.

For example, when using Collections.sort(List) with a List containing objects of a user-defined class A, then this class must implement the Comparable interface's method compareTo() to specify which property of the object will be used for comparisons/sorting.

Otherwise, a ClassCastException will be thrown because Java tries to cast the objects to a Comparable.
 
((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)

Open in new window


Note: The pre-defined classes like String, Integer, etc. already implement the interface Comparable, so any list containing objects of these classes do not throw a ClassCastException.

For comparing objects of a user-defined class, which does not implement Comparable and which you cannot edit (e.g., because you do not have source code or its a class file in a jar library), we can use a custom class implementing the interface Comparator that similar to Comparable has one method, compare, that takes both the object being compared and the object being compared to as arguments.

Java API reference:
Interface Comparable has one method to be implemented by the class using it: 

public int compareTo(Object o)
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer if this object is less than, equal to, or greater than the specified object respectively.

Interface Comparator has a method compare with the following syntax: 

public int compare(Object o1,Object o2)
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer if the first argument is less than, equal to, or greater than the second respectively.

Open in new window


The following two examples will show how similar comparison functionality can be achieved by using either of the two interfaces: Comparable and Comparator.

Example 1: Using Comparable
 
// Time3.java (using Comparable)
public class Time3 implements Comparable< Time3 >
{
   private int hour;   // 0 - 23
   private int minute; // 0 - 59
   private int second; // 0 - 59

   // Time3 no-argument constructor: initializes each instance variable
   // to zero; ensures that Time3 objects start in a consistent state  
   public Time3()                                                      
   {                                                                   
      this( 0, 0, 0 ); // invoke Time3 constructor with three arguments
   } // end Time3 no-argument constructor                              

   // Time3 constructor: hour supplied, minute and second defaulted to 0
   public Time3( int h )                                                
   {                                                                    
      this( h, 0, 0 ); // invoke Time3 constructor with three arguments 
   } // end Time3 one-argument constructor                              

   // Time3 constructor: hour and minute supplied, second defaulted to 0
   public Time3( int h, int m )                                         
   {                                                                    
      this( h, m, 0 ); // invoke Time3 constructor with three arguments 
   } // end Time3 two-argument constructor                              

   // Time3 constructor: hour, minute and second supplied   
   public Time3( int h, int m, int s )                      
   {                                                        
      setTime( h, m, s ); // invoke setTime to validate time
   } // end Time3 three-argument constructor                

   // Time3 constructor: another Time3 object supplied           
   public Time3( Time3 time )                                    
   {                                                             
      // invoke Time3 three-argument constructor                 
      this( time.getHour(), time.getMinute(), time.getSecond() );
   } // end Time3 constructor with a Time3 object argument       

   // Set Methods
   // set a new time value using universal time; ensure that
   // the data remains consistent by setting invalid values to zero
   public void setTime( int h, int m, int s )
   {
      setHour( h );   // set the hour
      setMinute( m ); // set the minute
      setSecond( s ); // set the second
   } // end method setTime

   // validate and set hour
   public void setHour( int h )
   {
      hour = ( ( h >= 0 && h < 24 ) ? h : 0 );
   } // end method setHour

   // validate and set minute
   public void setMinute( int m )
   {
      minute = ( ( m >= 0 && m < 60 ) ? m : 0 );
   } // end method setMinute

   // validate and set second
   public void setSecond( int s )
   {
      second = ( ( s >= 0 && s < 60 ) ? s : 0 );
   } // end method setSecond

   // Get Methods
   // get hour value
   public int getHour()
   {
      return hour;
   } // end method getHour

   // get minute value
   public int getMinute()
   {
      return minute;
   } // end method getMinute

   // get second value
   public int getSecond()
   {
      return second;
   } // end method getSecond

   // convert to String in universal-time format (HH:MM:SS)
   public String toUniversalString()
   {
      return String.format(
         "%02d:%02d:%02d", getHour(), getMinute(), getSecond() );
   } // end method toUniversalString

   // convert to String in standard-time format (H:MM:SS AM or PM)
   public String toString()
   {
      return String.format( "%d:%02d:%02d %s",
         ( (getHour() == 0 || getHour() == 12) ? 12 : getHour() % 12 ),
         getMinute(), getSecond(), ( getHour() < 12 ? "AM" : "PM" ) );
   } // end method toString

   public int compareTo( Time3 time1 )
   {
      int hourCompare = this.getHour() - time1.getHour(); // compare hour

      // test the hour first
      if ( hourCompare != 0 )
         return hourCompare;

      int minuteCompare =
         this.getMinute() - time1.getMinute(); // compare minute

      // then test the minute
      if ( minuteCompare != 0 )
         return minuteCompare;

      int secondCompare =
         this.getSecond() - time1.getSecond(); // compare second

      return secondCompare; // return result of comparing seconds
   }
} // end class Time3

Open in new window

// Sort a list using the custom Comparator class TimeComparator.
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

public class Sort4
{
   public void printElements()
   {
      List< Time3 > list = new ArrayList< Time3 >(); // create List

      list.add( new Time3(  6, 24, 34 ) );
      list.add( new Time3( 18, 14, 58 ) );
      list.add( new Time3(  6, 05, 34 ) );
      list.add( new Time3( 12, 14, 58 ) );
      list.add( new Time3(  6, 24, 22 ) );

      // output List elements
      System.out.printf( "Unsorted array elements:\n%s\n", list );

      // sort in order using a comparable            
      Collections.sort( list );

      // output List elements
      System.out.printf( "Sorted list elements:\n%s\n", list );
   } // end method printElements

   public static void main( String args[] )
   {
      Sort4 sort4 = new Sort4();
      sort4.printElements();
   } // end main
} // end class Sort4

Open in new window


Example 2: Using Comparator

public class Time2
{
   private int hour;   // 0 - 23
   private int minute; // 0 - 59
   private int second; // 0 - 59

   // Time2 no-argument constructor: initializes each instance variable
   // to zero; ensures that Time2 objects start in a consistent state  
   public Time2()                                                      
   {                                                                   
      this( 0, 0, 0 ); // invoke Time2 constructor with three arguments
   } // end Time2 no-argument constructor                              

   // Time2 constructor: hour supplied, minute and second defaulted to 0
   public Time2( int h )                                                
   {                                                                    
      this( h, 0, 0 ); // invoke Time2 constructor with three arguments 
   } // end Time2 one-argument constructor                              

   // Time2 constructor: hour and minute supplied, second defaulted to 0
   public Time2( int h, int m )                                         
   {                                                                    
      this( h, m, 0 ); // invoke Time2 constructor with three arguments 
   } // end Time2 two-argument constructor                              

   // Time2 constructor: hour, minute and second supplied   
   public Time2( int h, int m, int s )                      
   {                                                        
      setTime( h, m, s ); // invoke setTime to validate time
   } // end Time2 three-argument constructor                

   // Time2 constructor: another Time2 object supplied           
   public Time2( Time2 time )                                    
   {                                                             
      // invoke Time2 three-argument constructor                 
      this( time.getHour(), time.getMinute(), time.getSecond() );
   } // end Time2 constructor with a Time2 object argument       

   // Set Methods
   // set a new time value using universal time; ensure that
   // the data remains consistent by setting invalid values to zero
   public void setTime( int h, int m, int s )
   {
      setHour( h );   // set the hour
      setMinute( m ); // set the minute
      setSecond( s ); // set the second
   } // end method setTime

   // validate and set hour
   public void setHour( int h )
   {
      hour = ( ( h >= 0 && h < 24 ) ? h : 0 );
   } // end method setHour

   // validate and set minute
   public void setMinute( int m )
   {
      minute = ( ( m >= 0 && m < 60 ) ? m : 0 );
   } // end method setMinute

   // validate and set second
   public void setSecond( int s )
   {
      second = ( ( s >= 0 && s < 60 ) ? s : 0 );
   } // end method setSecond

   // Get Methods
   // get hour value
   public int getHour()
   {
      return hour;
   } // end method getHour

   // get minute value
   public int getMinute()
   {
      return minute;
   } // end method getMinute

   // get second value
   public int getSecond()
   {
      return second;
   } // end method getSecond

   // convert to String in universal-time format (HH:MM:SS)
   public String toUniversalString()
   {
      return String.format(
         "%02d:%02d:%02d", getHour(), getMinute(), getSecond() );
   } // end method toUniversalString

   // convert to String in standard-time format (H:MM:SS AM or PM)
   public String toString()
   {
      return String.format( "%d:%02d:%02d %s",
         ( (getHour() == 0 || getHour() == 12) ? 12 : getHour() % 12 ),
         getMinute(), getSecond(), ( getHour() < 12 ? "AM" : "PM" ) );
   } // end method toString
} // end class Time2

Open in new window


// Custom Comparator class that compares two Time2 objects.
import java.util.Comparator;

public class TimeComparator implements Comparator< Time2 >
{
   public int compare( Time2 time1, Time2 time2 )
   {
      int hourCompare = time1.getHour() - time2.getHour(); // compare hour

      // test the hour first
      if ( hourCompare != 0 )
         return hourCompare;

      int minuteCompare =
         time1.getMinute() - time2.getMinute(); // compare minute

      // then test the minute
      if ( minuteCompare != 0 )
         return minuteCompare;

      int secondCompare =
         time1.getSecond() - time2.getSecond(); // compare second

      return secondCompare; // return result of comparing seconds
   } // end method compare
} // end class TimeComparator

Open in new window


// Sort a list using the custom Comparator class TimeComparator.
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

public class Sort3
{
   public void printElements()
   {
      List< Time2 > list = new ArrayList< Time2 >(); // create List

      list.add( new Time2(  6, 24, 34 ) );
      list.add( new Time2( 18, 14, 58 ) );
      list.add( new Time2(  6, 05, 34 ) );
      list.add( new Time2( 12, 14, 58 ) );
      list.add( new Time2(  6, 24, 22 ) );

      // output List elements
      System.out.printf( "Unsorted array elements:\n%s\n", list );

      // sort in order using a comparator            
      Collections.sort( list, new TimeComparator() );

      // output List elements
      System.out.printf( "Sorted list elements:\n%s\n", list );
   } // end method printElements

   public static void main( String args[] )
   {
      Sort3 sort3 = new Sort3();
      sort3.printElements();
   } // end main
} // end class Sort3

Open in new window


The above two examples produces the same output.

Output:
 
Unsorted array elements:
[6:24:34 AM, 6:14:58 PM, 6:05:34 AM, 12:14:58 PM, 6:24:22 AM]
Sorted list elements:
[6:05:34 AM, 6:24:22 AM, 6:24:34 AM, 12:14:58 PM, 6:14:58 PM]

Open in new window


Conclusion:

Both Comparable and Comparator can be used to achieve sorting of a list of objects based on a particular property or field of that Object.
Many times you have to create objects of a pre-defined class which does not implement Comparable and which may be part of a standard API or otherwise unavailable / undesirable for source editing. In such cases you can sort the list containing such objects by using the interface Comparator.
4
Comment
Author:akjain
0 Comments

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Join & Write a Comment

This tutorial explains how to use the VisualVM tool for the Java platform application. This video goes into detail on the Threads, Sampler, and Profiler tabs.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Suggested Courses
Course of the Month15 days, 1 hour left to enroll

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month