有什么方法可以为List中的对象字段编写通用排序代码吗?

时间:2014-09-25 16:24:19

标签: java generics arraylist

在一次采访中我遇到了这个问题。

编写一个接受bean类字段的类,并根据传递的字段对包含bean对象的列表进行排序。

1)用于分类的技术应该是什么? 我回答了比较者。

2)我不希望为每个字段创建许多Comparator类。你能写一个适用于所有领域的通用比较器吗? 以下是我的代码。

请告诉我这是否是正确的做法,或者有更好的方法来做到这一点。如果我错了,我请你纠正我。

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

}

///员工类///

公共类员工{

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 + "]";
}

}

////根据tieTYT和radai的评论。我做了以下更改。如果有任何问题,请纠正我////

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

}

3 个答案:

答案 0 :(得分:1)

是的,您可以使用反射编写通用比较器。获得字段名称后,可以使用反射检索字段值。执行此操作后,您可以使用java中的所有基本值保持类(如Boolean,Integer,String,uuid,date等)的可比性,将使用反射获得的值转换为Comparable,并比较它们

答案 1 :(得分:1)

这可能是主观的,因为我无法阅读采访者的想法。但如果我是你,我会用反射找到这个领域。如果字段为Comparable,则使用该接口。否则,您将不得不询问面试官您希望它如何在某些类型的领域工作。

您的代码存在的问题是它非常特定于当前类。如果添加了新字段,除非您编写和编译新代码,否则它将无法对其进行排序。它也只适用于类。使用反射,几乎可以在任何类中使用它。

答案 2 :(得分:0)

为了做到这一点,你应该将getting要比较的值的概念抽象为接口(或Java 8中的函数),如下所示:

此函数将为您创建一个Comparator,使用getter Function来读取bean中的值:

public static <T> Comparator<T> createComparator(Function<T, R extends Comparable> getter)
{
  return (obj1, obj2) -> getter.apply(obj1).compareTo(getter.apply(obj2);
}

可以这样使用:

Collections.sort(employees, createComparator((employee) -> employee.getName()));

如果您已开始使用String名称来阅读该字段,请使用getter函数的反射实现,如下所示:

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

可以这样使用:

Collections.sort(employees, reflectiveGetter("name"));

完全相同的事情可以在使用接口的任何Java版本中实现,只是它有点冗长。