基于运行时属性对对象列表进行排序

时间:2012-11-26 15:05:37

标签: java list sorting reflection comparator

我有一个VO的arraylist。这些对象具有许多属性和相应的get / set方法。我想根据我将在运行时获得的属性对此数组列表进行排序。 让我详细解释一下。我的VO就像这样

public class Employee {
    String name;
    String id;

    private String getName() {
        return name;
    }

    private String getId() {
        return id;
    }
}

我将在运行时获取一个字符串'sortType',它可以是'id'或'name'。我想根据字符串的值对列表进行排序。

我曾尝试过使用比较器和反射,但没有运气。可能是我没有正确使用它。我不想使用if循环并创建新的比较器类。还有其他想法吗?

try catch应该在新类中。这是工作代码。如果你想为比较器使用一个单独的类,请在@ Bohemian的评论中找到它。

        String sortType = "name"; // determined at runtime
        Collections.sort(results, new Comparator<Employee>() {
        public int compare(Employee c1, Employee c2) {
            try{
            Method m = c1.getClass().getMethod("get" + StringUtils.capitalize(sortType));
            String s1 = (String)m.invoke(c1);
            String s2 = (String)m.invoke(c2);
            return s1.compareTo(s2);
            }
            catch (Exception e) {
                return 0;
            }
        }
       });

4 个答案:

答案 0 :(得分:16)

为作业创建Comparator

public class EmployeeComparator implements Comparator<Employee> {

    private final String type;

    public EmployeeComparator (String type) {
        this.type = type;
    }

    public int compare(Employee e1, Employee e2) {
        if (type.equals("name")) {
             return e1.getName().compareTo(e2.getName());
        }
        return e1.getId().compareTo(e2.getId());
    }

}

然后使用它

String type = "name"; // determined at runtime
Collections.sort(list, new EmployeeComparator(type));

反射版本会是类似的,除非你在&#34; get&#34;的对象上寻找方法。 + type(大写)并调用它并将其强制转换为Comparable并使用compareTo(我将尝试显示代码,但我使用的是我的iPhone并且它有点延伸,但是这里有)

public class DynamicComparator implements Comparator<Object> {
    private final String type;
    // pass in type capitalised, eg "Name" 
    // ie the getter method name minus the "get"
    public DynamicComparator (String type) {
        this.type = type;
    }
    public int compare(Object o1, Object o2) {
        // try-catch omitted 
        Method m = o1.getClass().getMethod("get" + type);
        String s1 = (String)m.invoke(o1);
        String s2 = (String)m.invoke(o2);
        return s1.compareTo(s2);
    }
}

好的......这里是如何做到的没有创建一个类,使用匿名类(有异常处理,所以代码编译):

List<?> list;
final String attribute = "Name"; // for example. Also, this is case-sensitive
Collections.sort(list, new Comparator<Object>() {
    public int compare(Object o1, Object o2) {
        try {
            Method m = o1.getClass().getMethod("get" + attribute);
            // Assume String type. If different, you must handle each type
            String s1 = (String) m.invoke(o1);
            String s2 = (String) m.invoke(o2);
            return s1.compareTo(s2);
        // simply re-throw checked exceptions wrapped in an unchecked exception
        } catch (SecurityException e) {
            throw new RuntimeException(e); 
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
});

答案 1 :(得分:2)

执行以下操作:

  • 从客户端
  • 获取字段的名称
  • 构建getter的名称 - &gt; “get”+字段名称(大写第一个字符后)
  • 尝试使用Class.getDeclaredMethod()
  • 找到带反射的方法
  • 如果找到,则在 VO 类的两个实例上调用返回的Method对象
  • 使用调用的getter方法的结果进行排序

答案 2 :(得分:0)

Keep it simple!

如果选择仅为idname - 请使用if语句。

两个选择之间的选择。这就是如果被发明的话。

或者,如果它是许多属性,则使用反射,或者首先将数据存储在Map中。有时Map比班级更好。特别是如果你的VO没有getter和setter以外的方法。

但是请注意,在这种情况下使用反射可能不安全,因为您的客户端可能会在类似于SQL注入的攻击中注入CGI参数中的任何术语。

答案 3 :(得分:0)

无论出于何种原因,您都不想使用if语句 ... 利用java.lang.reflect.Field

String sortType = "name"; // determined at runtime
Comparator<Employee> comparator = new Comparator<Employee>(){
    public int compare(Employee e0, Employee e1){
        try {
            Field sortField = e0.getClass().getField(sortType);
            return sortField.get(e0).toString()
                       .compareTo(sortField.get(e1).toString()));

        } catch(Exception e) {
            throw new RuntimeException(
                "Couldn't get field with name " + sortType, e);
        }
    };
}

但这仅适用于字符串字段-以下处理任何类型的字段(或至少Comparable个字段)

String sortType = "name"; // determined at runtime
Comparator<Employee> comparator = new Comparator<Employee>(){
    public int compare(Employee e0, Employee e1){
        try {
            Field sortField = e0.getClass().getField(sortType);
            Comparable<Object> comparable0
                = (Comparable<Object>) sortField.get(e0);
            Comparable<Object> comparable1
                = (Comparable<Object>) sortField.get(e1);

            return comparable0.compareTo(comparable1);

        } catch(Exception e) {
            throw new RuntimeException(
                          "Couldn't get field with name " + sortType, e);
        }
    };
}

**不过请注意,不会忽略String / Character / char 的大小写(尽管将占负数) )-例如,将小写字母"Z"前的大写字母"a"排序。 如果您想同时说明这两种情况,那么我看不出if语句的解决方法(等待...)!

在我的JavaSortAnyTopLevelValueObject Github项目(也可以在依赖JAR中获得)中,您可以看到处理所有这些情况的完整实现,以及以下情况:

  • 处理多个排序领带优先级字段(即:按优先级0优先排序,按优先级1排序第二个(如果是平局),按...排序第三)
  • 按每个排序参数处理升/降的排序
  • 处理{publicprimitiveBoxed Primitive}的任何顶级String(或从呼叫位置可见的修饰符)字段
  • 忽略字符大小写