Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Creating a GenericComparator in Java

In this article, we will see how to create a GenericComparator in Java to abstract the way we access Collections API.

In this article, although the subject is simple, we will be able to practice some interesting things like:

  • Reflection;
  • Generics;
  • Comparable and Comparator.

A very interesting feature (and powerful) is how we can organize lists of objects in Java. We all know that the key parts to this feature are the Comparable and Comparator interfaces. We will not go into details of how they work, because this is not our focus here, but it is noteworthy that, with Comparator, we can create comparators for each attribute of a class and, therefore, organizing a list of objects for that class in several different ways.

Well, this is where the problem begins to appear. Imagine you have a class with 20 attributes, requires that the lists with this class objects be sorted by, let us say, eight of these attributes. Well, what is the solution? Simple, just create a Comparator for each attribute and the problem is solved. However, you have another class that also needs to be ordered for 10 attributes. Then, we have created over 10 Comparators? Until now we already have 18 classes only for the comparators. In my opinion, the remedy is already doing more harm than the disease.

We will resolve it in a way a bit more elegant and easier to maintain, let's create a generic Comparator that can arrange any list of objects, simply for what their attributes are comparable.

Enough talking and let's go hands-on: our little project will have 3 packages:

  • domain: where we will place our domain classes;
  • util: where we will put our utility classes (including our comparator);
  • main: where we will make our test application.

First, to get a little closer to the real world, we will create our base class for our entities, like in Listing 1.

Listing 1. Abstract Entity class.

package domain;  
  
// imports omitted  
public abstract class Entity implements Serializable {  
  
   private static final long serialVersionUID = 1L;  
     
   protected Integer id = null;  
   protected Date registryDate = null;  
     
   // gets and sets omitted
  
}  

Nothing different here, we just created an abstract class that is the superclass for of all our entities. Now we can create our entities (Listings 2 and 3).

Listing 2. Client entity.

package domain;  
  
public class Client extends Entity {  
  
   private static final long serialVersionUID = 1L;  
     
   private String  name;  
   private String  address;  
     
   //gets e sets omitted 
  
   @Override  
   public String toString() {  
       return getId() + " - "  + getName() + " - " + getAddress();  
   }  
}  

Listing 3. Supplier entity.

package domain;  
  
public class Supplier extends Entity {  
  
   private static final long serialVersionUID = 1L;  
  
   private String socialReason  = null;  
   private String fantasyName = null;  
     
   //gets e sets omitted  
   @Override  
   public String toString() {  
      return "Supplier [fantasyName=" + fantasyName + ", socialReason="  
            + socialReason + ", id=" + id + "]";  
   }  
}  

Here they are. Of course, they are very simplified, in the real world, you cannot forget to override the equals and hashCode methods, and to define a strategy to force each of our entities to implement Comparable interface, among others.

Now that we finished with the entities, we will create our utility classes (Listing 4).

Listing 4. Sorting utility class.

package util;  
  
/** 
 * Define what type of ascending (ASC) or descending (DESC) order 
 */  
public enum SortType {  
   ASC(1), DESC(-1);  
     
   private int index = 1;  
     
      
   private SortType(int index) {  
      this.index = index;  
   }  
     
   public int getIndex() {  
      return this.index;  
   }  
}  

This enum will be used so that we can determine if the sort is ascending or descending.

We will see now the index function set here. Let us see another utility class (Listing 5).

Listing 5. Reflection utility class.

package util;  
/** 
 * Utility class for use with Reflection 
 */  
public class ReflectionUtil {  
  
   /** 
    * Build the get method name, according to the attribute name
    * @param fieldName 
    * @return get attribute method
    */  
   public static String buildGetMethodName(String fieldName) {  
      StringBuilder methodName = new StringBuilder("get");  
      methodName.append(fieldName.substring(0, 1)  
            .toUpperCase());  
      methodName.append(fieldName.substring(1, fieldName.length()));  
      return methodName.toString();  
   }  
     
   /** 
    * Build the name of the set method, according to the attribute name
    * @param fieldName 
    * @return set attribute method
    */  
   public static String buildSetMethodName(String fieldName) {  
      StringBuilder methodName = new StringBuilder("set");  
      methodName.append(fieldName.substring(0, 1)
.toUpperCase());  
      methodName.append(fieldName.substring(1, fieldName.length()));  
      return methodName.toString();  
   }  
        
}  

This class aids in the use of Reflection. It has two methods, one for the set construction method of the name attribute and the other for the construction of the get method name. The methods work in a simply way. Take the first letter of the attribute name and convert it to uppercase and then add the prefix "set" to the set method and "get" to the get method. When recalling that, the class should be encapsulated following the standard JavaBeans naming.

Now, we reached our goal, we will now create our generic comparator, like in Listing 6.

Listing 6. Generic comparator.

package util;  
  
//imports omitted  
  
/** 
 * Generic Comparator to order any Entity 
 * 
 * @param <T> class to be ordered
 */  
public class GenericComparator<T extends Entity> implements Comparator<T> {  
     
   private SortType sortType   = null;  
   private String   methodName = null;  
     
   /** 
    * Construct a GenericComparator according to field and type 
    * @param sortField field for ordering
    * @param sortType ascending (ASC) or descending (DESC)
    */  
   public GenericComparator(String sortField, SortType sortType) {  
      this.sortType   = sortType;  
      this.methodName = ReflectionUtil.buildGetMethodName(sortField);  
   }  
  
  
   @SuppressWarnings("unchecked")  
   @Override  
   public int compare(T o1, T o2) {  
      try {  
         Method method1 = o1.getClass().getMethod(this.methodName,new Class[]{});  
         Comparable comp1 = (Comparable) method1.invoke(o1, new Object[]{});  
           
         Method method2 = o1.getClass().getMethod(this.methodName, new Class[]{});  
         Comparable comp2 = (Comparable) method2.invoke(o2, new Object[]{});  
           
         return comp1.compareTo(comp2) * this.sortType.getIndex();  
      } catch (Exception e) {  
         e.printStackTrace();  
         throw new RuntimeException(e.getMessage());  
      }  
   }  
     
   /** 
    * Organizes the List<T> according to the field and the type (ASC or DESC)
    *  
    * @param <T> class of objects to be sorted
    * @param list List<T> to be ordered
    * @param sortField field for ordering
    * @param sortType type of ordering (ASC ou DESC) 
    */  
      
   public static <T extends Entity> void sortList(List<T> list, String sortField,   
         SortType sortType) {  
      GenericComparator<T> comparator = new GenericComparator<T>(sortField,   
            sortType);  
      Collections.sort(list, comparator);  
   }  
     
   /** 
    * Organise a T[] according with the field and the type (ASC or DESC)) 
    *  
    * @param <T> Class of objects to be sorted
    * @param array  T[] array to be ordained
    * @param sortField field for ordering
    * @param sortType type of ordering (ASC ou DESC) 
    */ 
   public static <T extends Entity> void sortArray(T[] array, String sortField,   
         SortType sortType) {  
      GenericComparator<T> comparator = new GenericComparator<T>(sortField,   
            sortType);  
      Arrays.sort(array, comparator);  
   }  
}  

Our GenericComparator is a generic class that can compare any class since it is an entity of our system. Its operation is quite simple, in the constructor we defined the attribute that will be used for sorting and the type of this order, which was defined in our enum SortType. Still in the constructor, we already found out what’s the name of the get method of the attribute that will be used for sorting, (we use our ReflectionUtil for this). Once it is a class that implements Comparator, we need to override the Compare method, and this is where things really happen.

First, we discovered by reflection, what’s the get method name of the first object (which is easy, because we already know what the name of this method). Then, we call it and keep its value as a Comparable.

Method method1 = o1.getClass().getMethod(this.methodName,new Class[]{});  
Comparable comp1 = (Comparable) method1.invoke(o1, new Object[]{});  

We do the same process for the second object.

Method method = 2 o1.getClass () getMethod (this.methodName, new Class [] {}).;
Comparable 2comp = (Comparable) method2.invoke (o2, new Object [] {});

Now that we have the two values as Comparables, we can compare with each other and return the result of this comparison.

return comp1.compareTo(comp2) * this.sortType.getIndex();  

This is where we use the index of our enum. As we know, by definition, the compare method should return an integer greater than 1 if the first object is greater than the second, 0 if they are equal, an integer less than 1 if the first is less than the second. When we set our kind of sorting as ASC, the index is 1, that is, nothing will be changed in order. Nevertheless, when we set the SortType as DESC, the index is -1, and when the result is multiplied by this index, the rule is reversed and the list sorted descending.

The sortList and sortArray methods are only facilitators, since we know that a Comparator must be passed to the helper classes Collections and Arrays in order to get them sorted in a different way than the natural. With them, we can write only this:

List<Cliente> list = new ArrayList<Cliente>();  
//.. the list filling  
GenericComparator.sortList(list, "name", SortType.ASC);

rather than:

List<Cliente> list = new ArrayList<Cliente>();  
//.. the list filling
GenericComparator<Client> comparator = new GenericComparator<Client>("name", SortType.ASC);  
Collections.sort(list, comparator);

That is, it is only a shortcut. Now, we just need our application to test (Listing 7).

Listing 7. App main class.

package main;  
  
//imports omitted
  
public class App {  
  
     
    public static void main(String[] args) {  
        
        
   List<Client> list = new ArrayList<Client>();   
        
   Client client = new Client();  
         
   client.setId(3);  
   client.setName("John");  
   client.setAddress("12 Street");  
                
   lista.add(client);  
         
   client = new Client();
   client.setId(1);  
   client.setName("Mary");  
   client.setAddress("Park Avenue");  
         
   list.add(client);  
         
   client = new Client();
   client.setId(2);  
   client.setName("Fulano");  
   client.setAddress("Bool Street");  
         
   list.add(client);  
         
   GenericComparator.sortList(list, "address", SortType.ASC);  
         
   Iterator it = list.iterator();  
         
   while (it.hasNext()) {  
       Client cli = (Client) it.next();  
       System.out.println(cli);  
   }  
    }  
}  

Well, that is it. Remember that we do not enter the merits of modeling the domain classes; the created ones here were only to test our GenericComparator.



Web developer and passioned for web design, SEO and front end technologies.

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login