2

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.

2
  • 1
    It is clumsy. Use some libraries like Google Guava instead (or Java 8). Commented Dec 21, 2014 at 15:13
  • 1
    Un-neat code that does the trick should look something like that: if (o1.field1 < o2.field2) return -1; else if (o1.field1 > o2.field1) return 1; else if (o1.field2 < o2.field2) return -1; else if (o1.field1 > o2.field2) return 1; else if (o1.field3 < o2.field3) return -1 else (o1.field3 > o2.field3) return 1; else return 0; The idea is to use multiple if-else condition in the desired comparison order (most important field first) Commented Dec 21, 2014 at 15:15

2 Answers 2

9

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"));
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your response. Similarly is there any way to sort in Java 1.6 or 1.7
5

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();  
}  

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.