Sai Tata
Sai Tata

Reputation: 47

How to sort all fields in an object using only one Comparator?

How to sort all fields in an object using a single comparator?

Ex: If I have an Employee object with three fields such as Name, Eid and Salary. Instead of writing three comparators i.e. Namecomparator, Eidcomparator and Salarycomparator for sorting, I need only only one comparator that can sort based on the filed I provide dynamically.

Upvotes: 2

Views: 1132

Answers (2)

tobias_k
tobias_k

Reputation: 82899

Instead of writing 3 comparator's i.e., Namecomparotor, Eidcomparotor and Salarycomparotor for sorting, I need only only one comparotor that can sort based on the filed I provide dynamically.

Using Java 8, and if all those fields have a get method, you can use Comparator.comparing

Collections.sort(employees, Comparator.comparing(Employee::getSalary);

And if you want to sort by multiple criteria (to break ties), you can use thenComparing

Collections.sort(employees, Comparator.comparing(Employee::getSalary)
                                      .thenComparing(Employee::getName)
                                      .thenComparing(Employee::getId));

In both cases, you just pass the field to sort by as a Function to create a custom Comparator. Alternatively, you can also use a lambda expression to sort by all kinds of other criteria:

Collections.sort(employees, Comparator.comparing(e -> e.getName.length()));

In case you are using an older version of Java (and can not upgrade to Java 8), you could make your own Function class and an according generic Comparator:

abstract class CompareFunction<A> {
    abstract public Comparable apply(A object);
}

class FunctionComparator<T> implements Comparator<T> {
    final CompareFunction<T> function;
    public FunctionComparator(CompareFunction<T> function) {
        this.function = function;
    }
    public int compare(T a, T b) {
        return function.apply(a).compareTo(function.apply(b));
    }
}

Usage:

Collections.sort(employees, new FunctionComparator<Employee>(new CompareFunction<Employee>() {
    public Comparable apply(Employee object) {
        return object.getSalary();
    };
}));

Or combine it with Java Reflextion to create such a comparator based on the name of a getter method:

public static <T> Comparator<T> createGetterComparator(Class<T> clazz, String getterName) throws Exception {
    final Method getter = clazz.getMethod(getterName);
    return new FunctionComparator<T>(new CompareFunction<T>() {
        public Comparable apply(T object) {
            try {
                return (Comparable) getter.invoke(object);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    });
}

Usage:

Collections.sort(employees, createGetterComparator(Employee.class, "getSalary"));

Upvotes: 9

SMA
SMA

Reputation: 37023

Use something like sort by id, then name then salary:

public class EmployeeComparator implements Comparator<Employee> {
    public int compare(Employee e1, Employee e2) {
        if (e1 == e2) {
           return 0;
        } 
        if (e1 == null && e2 != null) {
            return -1;
        }
        if (e1 != null && e2 == null) {
            return -1;
        }
        if (e1.getId().equals(e2.getId())) {
            if (e1.getName().equals(e2.getName())) {
                if (e1.getSalary() == e2.getSalary()) {
                    return 0;
                } else {
                    return int(e1.getSalary() - e2.getSalary());
                }
            } else {
                 return e1.getName().compareTo(e2.getName());
            }
        } else {
            return e1.getId().compareTo(e2.getId());
        }
    }
}

If you need concise version of above, you will need to either use Guava's ComparisonChain as below:

    @Override  
    public int compare(Employee e1, Employee e2) {  
        return ComparisonChain.start() 
           .compare(e1.getId(), e2.getId(), Ordering.natural().nullsLast()) 
           .compare(e1.getName(), e2.getName(), Ordering.natural().nullsLast()) 
           .compare(e1.getSalary(), e2.getSalary(), Ordering.natural().nullsLast()) 
           .result(); 
   }  

or apache's CompareToBuilder as below:

public int compare(Employee e1, Employee e2) {  
    return new CompareToBuilder().append(e1.getId(), e2.getId()).append(e1.getName(), e2.getName()).append(e1.getSalary(), e2.getSalary()).toComparison();  
}  

Upvotes: 5

Related Questions