Reputation: 5
In an interview i came across this question.
Write a class which accepts a field of a bean class and sorts a list containing bean objects according to the field passed.
1) What should be the technique used for sorting? I answered Comparator.
2) I don't want many Comparator classes created for each field. Can you write a generic Comparator which works for all fields. Below is my code.
Please let me know if this is a correct way of doing or is there better way to do this. I request you to correct me if i am wrong.
public class GenericComparatorDemo {
static List<Employee> al = new ArrayList<Employee>();
static{
al.add(new Employee(45, "Vijay", "Bangalore", "Banking", 88, 99999));
al.add(new Employee(13, "Manoz", "Chennai", "Insurance", 48, 28000));
al.add(new Employee(79, "Ajay", "Hyderabad", "Real Estate", 54, 24000));
al.add(new Employee(21, "Sindu", "Noida", "Analyst", 89, 99998));
al.add(new Employee(67, "Honey", "Mumbai", "Social", 88, 111111));
al.add(new Employee(12, "Lucky", "Mysore", "Social", 86, 99997));
}
/**
* @param args
*/
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
System.out.println("Please enter the field on which you want to sort employee's...");
final String input = scn.nextLine();
if(null != input && !"".equals(input)){
Collections.sort(al, new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
if("id".equals(input)){
return (o1.getId() < o2.getId()) ? -1 : ((o1.getId() == o2.getId()) ? 0 : 1);
}else if("name".equals(input)){
return o1.getName().compareTo(o2.getName());
}else if("location".equals(input)){
return o1.getLocation().compareTo(o2.getLocation());
}else if("department".equals(input)){
return o1.getDepartment().compareTo(o2.getDepartment());
}else if("rewardPoints".equals(input)){
return (o1.getRewardPoints() < o2.getRewardPoints()) ? -1 : ((o1.getRewardPoints() == o2.getRewardPoints()) ? 0 : 1);
}else if("salary".equals(input)){
return (o1.getSalary() < o2.getSalary()) ? -1 : ((o1.getSalary() == o2.getSalary()) ? 0 : 1);
}else{
return 0;// when proper field is not entered sorting will not happen
}
}
});
}else{
System.out.println("Please enter valid employee field to sort employee's...");
}
for(Employee alObj:al){
System.out.println("\n" + alObj.toString());
}
}
}
/// Employee Class ///
public class Employee {
private long id;
private String name;
private String location;
private String department;
private int rewardPoints;
private double salary;
public Employee(long id, String name, String location, String department,
int rewardPoints, double salary) {
this.id = id;
this.name = name;
this.location = location;
this.department = department;
this.rewardPoints = rewardPoints;
this.salary = salary;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public int getRewardPoints() {
return rewardPoints;
}
public void setRewardPoints(int rewardPoints) {
this.rewardPoints = rewardPoints;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", location="
+ location + ", department=" + department + ", rewardPoints="
+ rewardPoints + ", salary=" + salary + "]";
}
}
//// According to comments from tieTYT and radai. I have made the following changes. Please correct me if any thing is wrong ////
public class GenericComparatorReflectionDemo {
static List<Employee> al = new ArrayList<Employee>();
static{
al.add(new Employee(45, "Vijay", "Bangalore", "Banking", 88, 99999));
al.add(new Employee(13, "Manoz", "Chennai", "Insurance", 48, 28000));
al.add(new Employee(79, "Ajay", "Hyderabad", "Real Estate", 54, 24000));
al.add(new Employee(21, "Sindu", "Noida", "Analyst", 89, 99998));
al.add(new Employee(67, "Honey", "Mumbai", "Social", 88, 111111));
al.add(new Employee(12, "Lucky", "Mysore", "Social", 86, 99997));
}
/**
* @param args
*/
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
System.out.println("Please enter the field on which you want to sort employee's...");
final String input = scn.nextLine();
if(null != input && !"".equals(input)){
Collections.sort(al, new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
try {
Field employeeField = Employee.class.getDeclaredField(input);
employeeField.setAccessible(true);
Comparable employeeFieldValue1 = (Comparable)employeeField.get(o1);
Comparable employeeFieldValue2 = (Comparable)employeeField.get(o2);
return employeeFieldValue1.compareTo(employeeFieldValue2);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
// when proper field is not entered sorting or any exception occurs
return 0;
}
}
});
}else{
System.out.println("Please enter valid employee field to sort employee's...");
}
for(Employee alObj:al){
System.out.println("\n" + alObj.toString());
}
}
}
Upvotes: 0
Views: 958
Reputation: 34321
To do this neatly you should abstract the concept of getting
the value to compare into an interface (or function in Java 8) as follows:
This function will create you a Comparator
that uses the getter
Function
to read the value from the a bean:
public static <T> Comparator<T> createComparator(Function<T, R extends Comparable> getter)
{
return (obj1, obj2) -> getter.apply(obj1).compareTo(getter.apply(obj2);
}
That can be used like this:
Collections.sort(employees, createComparator((employee) -> employee.getName()));
If you're set on using String
names to read the field then use just use a reflective implementation of the getter
function like this:
private Function<Employee, Comparable> reflectiveGetter(String fieldName) throws NoSuchFieldException
{
Field field = Employee.class.getDeclaredField(fieldName);
field.setAccessible(true);
return (employee) ->
{
try
{
return (Comparable)field.get(employee);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
}
That can be used like this:
Collections.sort(employees, reflectiveGetter("name"));
The exact same thing can be acheived in any version of Java using interfaces only it's a bit more verbose.
Upvotes: 0
Reputation: 67514
This may be subjective as I can't read the interviewer's mind. But if I were you, I'd use reflection to find the field. If the field is Comparable
, then use that interface. Otherwise, you'll have to ask the interviewer how you want it to work on certain types of fields.
The problem with your code is it's very specific to the current class. If a new field gets added, it won't be able to sort on that unless you write and compile new code. It also only works on this class. Using reflection, you could have it work on almost any class.
Upvotes: 1
Reputation: 24202
Yes, you can write a general purpose comparator using reflection. Once you have the field name you can retrieve the field value using reflection. After you do that you can use the fact that all the basic value holding classes in java (things like Boolean, Integer, String, uuid, date etc) are comparable, cast the values you got using reflection to Comparable, and compare them
Upvotes: 1