Java Collections - Comparable Vs Comparator

Published:
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
8,547 Views

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.