Google

Jun 29, 2014

Can you write an abstract Java comparator that will work with multiple fields to sort on?

Comparators are used in Java to custom sort objects. You need good coding skills to write abstract classes.

Q. Can you write an abstract Comparator class that will allow more specific comparator classes like EmployeeComparator to sort an object like Employee shown below?


package test;

public class Employee {
 
 private String firstName;
 private String surname;
 private int age;
 
 public Employee(String firstName, String surname, int age, Gender gender) {
  super();
  this.firstName = firstName;
  this.surname = surname;
  this.age = age;
 }
 protected String getFirstName() {
  return firstName;
 }
 protected void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 protected String getSurname() {
  return surname;
 }
 protected void setSurname(String surname) {
  this.surname = surname;
 }
 protected int getAge() {
  return age;
 }
 protected void setAge(int age) {
  this.age = age;
 }
 
 @Override
 public String toString() {
  return "Employee [firstName=" + firstName + ", surname=" + surname + ", age=" + age + "]\n";
 }
 
}


A. Pay attention to the Generics, inner class, and access control modifiers. It may look easy, but try writing it yourself.

Step 1:  Create the "AbstractGenericComparator" that can be extended by more specific comparator classes to extend.

 package test;

import java.util.Comparator;

/**
 * class is abstract, hence should have at least 1 abstract method. 
 * T is the type of object to compare 
 * S is the type of SortBy that defines --> Field and order for sorting
 */
public abstract class AbstractGenericComparator<T, S> implements Comparator<T> {

 // array of multiple sort by fields
 private SortBy<S>[] sortBy;
 
 @SafeVarargs
 //takes var args 1 or more SortBy fields
 public AbstractGenericComparator(SortBy<S>... sortBy) {
  //fail fast & precondition check
  if (sortBy.length == 0) {
   throw new IllegalArgumentException("At least one SortByr must be supplied!");
  }
  this.sortBy = sortBy;
 }

 @Override
 public int compare(T o1, T o2) {
  int result = 0;
  //sort by 1 or more fields.
  for (int index = 0; index < this.sortBy.length; index++) {
            result = compare(this.sortBy[index], o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return result;
 }
 
 protected SortBy<S>[] getSortBy() {
  return sortBy;
 }

 protected abstract int compare(SortBy<S> sortBy, T o1, T o2);

 // static inner class
 public static class SortBy<S> {
 
  private S field;
  private boolean isAscending = true;
  
  protected S getField() {
   return field;
  }
  protected void setField(S field) {
   this.field = field;
  }
  protected boolean isAscending() {
   return isAscending;
  }
  protected void setAscending(boolean isAscending) {
   this.isAscending = isAscending;
  }
 }

}

Step 2: Create the EmployeeComparator that extends AbstractGenericComparator.

package test;

public class EmployeeComparator<T,S> extends
  AbstractGenericComparator<Employee, String> {

 @SafeVarargs
 public EmployeeComparator(SortBy<String>... sortBy) {
  super(sortBy);
 }
 
 @Override
 /**
  * simplified. Not handling null, etc at this point
  * Assuming that firstname, surname, and age are mandatory fields.
  */
 protected int compare(SortBy<String> sortBy, Employee o1, Employee o2) {

  Employee e1 = (Employee) o1;
  Employee e2 = (Employee) o2;

  //Java 7 switch statement supports String
  switch (sortBy.getField()) {
  case "firstName":
   if (sortBy.isAscending()) {
    return e1.getFirstName().compareTo(e2.getFirstName());
   } else {
    return -e1.getFirstName().compareTo(e2.getFirstName());
   }
  case "surname":
   if (sortBy.isAscending()) {
    return e1.getSurname().compareTo(e2.getSurname());
   } else {
    return -e1.getSurname().compareTo(e2.getSurname());
   }
   
  case "age":
   if (sortBy.isAscending()) {
    return new Integer(e1.getAge()).compareTo(e2.getAge());
   } else {
    return  -new Integer(e1.getAge()).compareTo(e2.getAge());
   }
  default:
   throw new UnsupportedOperationException("Reached unsupported comparison");
  }

 }
}


Step 3: Write a test class with main method.

package test;



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

import test.AbstractGenericComparator.SortBy;

public class ComparatorTest {

 public static void main(String[] args) {
  
  //create Employee objects
  Employee emp1 = new Employee("Paul", "Smith", 15, Employee.Gender.Male);
  Employee emp2 = new Employee("Emma", "Smith", 28, Employee.Gender.Female);
  Employee emp3 = new Employee("Emma", "Smith", 18, Employee.Gender.Female);
  Employee emp4 = new Employee("John", "Anders", 17, Employee.Gender.Male);

  List<Employee> employees = new ArrayList<Employee>();
  employees.add(emp1);
  employees.add(emp2);
  employees.add(emp3);
  employees.add(emp4);

  //Create SortBY objects
  SortBy<String> sortByFirstName = new SortBy<String>();
  sortByFirstName.setField("firstName");

  SortBy<String> sortBySurname = new SortBy<String>();
  sortBySurname.setField("surname");

  SortBy<String> sortByAge = new SortBy<String>();
  sortByAge.setField("age");

  System.out.println("Before sorting");
  System.out.println(employees);
  
  //Create an EmployeeComparator
  EmployeeComparator<Employee, String> sorter = 
   new EmployeeComparator<Employee, String>(sortByFirstName,sortBySurname, sortByAge);
    
  Collections.sort(employees, sorter);
        
  System.out.println("\nafter sorting by firstName, surname, and age");
  System.out.println(employees);
  
  //Create another EmployeeComparator
  EmployeeComparator<Employee, String> sorter2 = 
    new EmployeeComparator<Employee, String>(sortByAge,sortByFirstName,sortBySurname);

  
  Collections.sort(employees, sorter2);
  
  System.out.println("\nafter sorting by age,surname, and firstname ");
  System.out.println(employees);
  
 }
}

Output:

Before sorting
[Employee [firstName=Paul, surname=Smith, age=15]
, Employee [firstName=Emma, surname=Smith, age=28]
, Employee [firstName=Emma, surname=Smith, age=18]
, Employee [firstName=John, surname=Anders, age=17]
]

after sorting by firstName, surname, and age
[Employee [firstName=Emma, surname=Smith, age=18]
, Employee [firstName=Emma, surname=Smith, age=28]
, Employee [firstName=John, surname=Anders, age=17]
, Employee [firstName=Paul, surname=Smith, age=15]
]

after sorting by age,surname, and firstname 
[Employee [firstName=Paul, surname=Smith, age=15]
, Employee [firstName=John, surname=Anders, age=17]
, Employee [firstName=Emma, surname=Smith, age=18]
, Employee [firstName=Emma, surname=Smith, age=28]
]


Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home