Google

Sep 6, 2012

Java coding to compare and sort objects

--> It is very common in real life coding to compare objects and sort objects in a collection. For example, retrieve the data from a database, and display the data differently by sorting by different fields. The PDF or xls reports can be generated by sorting the data as per user input. Java provides a number of classes like Collections and interfaces like Comparable and Comparator to sort objects within a collection. Here are a few questions and answers in sorting objects in Java.

Q. Can you write a simple program that compares two objects to return if they are equal or not? This method will be handy in defining your own equals( ) method.
A. This is usefull in domain or value object class to compare different object fields in an equals method


public class DomainObject {
 
  //protected because only inheriting domain classes can use it
  protected boolean isPropertyEqual(Object compare1, Object compare2) {
     // go here if compare1 is null, i.e. test cases 1 & 3
        if (compare1 == null) {
             if (compare2 != null) {
                 return false;
              }
        //go here if compare1 is not null, i.e. test cases 2 & 5     
        } else if (!compare1.equals(compare2)) {
             return false;
        }
        
        return true;      //test cases 1 & 4
   }
  
  public static void main(String[] args) {
     DomainObject d =  new DomainObject();
     Print(d.isPropertyEqual(null, null));  //test case 1
     Print(d.isPropertyEqual("abc", null)); //test case 2
     Print(d.isPropertyEqual(null, "abc")); //test case 3
     Print(d.isPropertyEqual("abc", "abc"));//test case 4
     Print(d.isPropertyEqual("abc", "cba"));//test case 5
 }
  
  
 public static void Print(boolean bol){
     System.out.println(bol);
 }
}

Note: The above class must be abstract. It was not tagged abstract to demo via the main() method by creating a new DomainObject(). The above method can be used in an extending class like

public class Security extends DomainObject implements Serializable {
    
    private String id; 

    //skipping other methods like getter/setter, toString, etc
   
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Security)) {
            return false;
        }
  
  //calling super class handy method we just created
        return isPropertyEqual(this.id, ((Security) obj).getId());
    }
 
  public int hashCode() {
        return id.hashCode();
    }

}


Q. Can you write code to sort the following string values naturally (i.e. in alphabetical order)?

   JEE, Java, Servlets, JMS, JNDI, JDBC, JSP, and EJB.
 
A. Here is the sample code that makes use of the default compareTo( ) provided in the String class as it implements the Comparable interface and the Collections utility class that provides a sorting method, which internally uses the efficient "merge sort" algorithm.

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Sort1 {

 public static void main(String[] args) {
  List<string> values = Arrays.asList("JEE", "Java", "Servlets", "JMS", "JNDI", "JDBC", "JSP", "EJB");
  Collections.sort(values); // uses the default compareTo(String anotherString)  in the String class 
  System.out.println(values);
 }
}



Output:

[EJB, JDBC, JEE, JMS, JNDI, JSP, Java, Servlets]

Q. Is there anything wrong with the above code?
A. Yes, 2 things -- firstly, the above sort is case sensitive, that is the uppercase takes priority over lowercase pushing 'Java' after 'JSP'. Secondly, if the collection had any null values, it will throw a NullpointerException.

These two issues can be rectified by providing a custom sorting implementation that ignores case and handles null values by pushing them to the end. The Collections class's sort method takes a Comparator implementation as a second argument. In the code below, the Comparator has been implemented as an anonymous inner class. The compare(...) method will be called a number of times by the Collections.sort(list, comparator).

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Sort2 {

 public static void main(String[] args) {
  List<String> values = Arrays.asList("JEE", "Java", null,  "Servlets", null, "JMS", "JNDI", "JDBC", "JSP", null,"EJB");
  
  //The comparator is defined as an anonymous inner class, but it can be
  //defined in its own class. Handles nulls and ignores case
  Collections.sort(values, new Comparator<String>() {

   @Override
   public int compare(String o1, String o2) {
    //push the null values to the end
    if(o1 == null){
     if(o2 == null) {
      return 0;
     }
     return 1;
    }
    else if(o2 == null){
     return -1;
    }
    return o1.compareToIgnoreCase(o2);
   }
  }); // anonymous inner class end
  
  System.out.println(values);
 }
}



Q. Now, what if you have your own custom class like a Dog, Cat, etc instead of a library class like String, Integer, etc?
A. Here is an example of a JavaTechnology custom object that implements a default sorting logic based on the rank (i.e popularity).

public class JavaTechnology implements Comparable<JavaTechnology>{
 
 private String name;
    private int rank;   // popularity lower value means more popular
 
    public JavaTechnology(String name, int rank){
        this.name = name;
        this.rank = rank;
    }

    //default implementation by rank alone
 @Override
 public int compareTo(JavaTechnology technology) {
  
  int rank1 = this.rank;
        int rank2 = technology.rank;
 
        if (rank1 > rank2){
            return +1;
        }else if (rank1 < rank2){
            return -1;
        }else{
            return 0;
        }
 }
 

    //required for printing, displaying, etc.
 @Override
 public String toString() {
  return "(" + name + " , " + rank + ")";
 }
}



Now, a simple test class
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Sort3Test {
 
 public static void main(String[] args) {
  
  JavaTechnology jt1 = new JavaTechnology("JEE", 1);
  JavaTechnology jt2 = new JavaTechnology("Java", 1);
  JavaTechnology jt3 = new JavaTechnology("Servlets", 2);
  JavaTechnology jt4 = new JavaTechnology("JSP", 2);
  JavaTechnology jt5 = new JavaTechnology("JNDI", 3);
  JavaTechnology jt6 = new JavaTechnology("EJB", 4);
  JavaTechnology jt7 = new JavaTechnology("JMS", 5);

  List<javatechnology> values = Arrays.asList(jt1, jt2, jt3, jt4, jt5, jt6, jt7);
  
  Collections.sort(values); // invokes the compareTo(...) method in JavaTechnology a number of times
  
  System.out.println(values);
  
 }
 
}



Output:

[(JEE , 1), (Java , 1), (Servlets , 2), (JSP , 2), (JNDI , 3), (EJB , 4), (JMS , 5)]


Q. What if you have a specific scenario where you want to first sort by rank and then alphabetically if the ranks are same?
A. Any number of Comparator classes can be created to sort them differently as shown below.

import java.util.Comparator;

public class JavaTechnologyComparator implements Comparator<JavaTechnology> {

 @Override
 public int compare(JavaTechnology t1, JavaTechnology t2) {

  //handle null values here
  Integer rank1 = t1.getRank();
  Integer rank2 = t2.getRank();
  
  int rankVal = rank1.compareTo(rank2);
  int nameVal = t1.getName().toLowerCase().compareTo(t2.getName().toLowerCase());
  
  //if same rank, then sort by name
  if(rankVal == 0){
   return nameVal;
  }
  
  //else sort by rank
  return rankVal ;
 }

}


Now, the Sort3Test class has been slightly modified to use a Comparator .

import java.util.Collections;
import java.util.List;

public class Sort3Test {
 
 public static void main(String[] args) {
  
  JavaTechnology jt1 = new JavaTechnology("JEE", 1);
  JavaTechnology jt2 = new JavaTechnology("Java", 1);
  JavaTechnology jt3 = new JavaTechnology("Servlets", 2);
  JavaTechnology jt4 = new JavaTechnology("JSP", 2);
  JavaTechnology jt5 = new JavaTechnology("JNDI", 3);
  JavaTechnology jt6 = new JavaTechnology("EJB", 4);
  JavaTechnology jt7 = new JavaTechnology("JMS", 5);

  List<JavaTechnology> values = Arrays.asList(jt1, jt2, jt3, jt4, jt5, jt6, jt7);
  
  Collections.sort(values, new JavaTechnologyComparator()); // invokes the compare(...) in JavaTechnologyComparator
                                                            // a number of times
  
  System.out.println(values);
  
 }
 
}


The output will be:

[(Java , 1), (JEE , 1), (JSP , 2), (Servlets , 2), (JNDI , 3), (EJB , 4), (JMS , 5)]
This should now enable you to sort any Java objects.

-->

Labels: ,

2 Comments:

Blogger Java Interview Questions said...

nice article, useful!

3:30 PM, January 27, 2013  
Blogger Sandeep Bhandari said...

If we start listing down the scenario where we can use the compareTo method for adding sorting behavior. The list is going to grow like anything. But one place where compareTo or Comparable interface isn't much useful is when compairing objects from third party classes.

6:35 PM, July 29, 2013  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home