我有很多这样的课程:
Class Person {
protected String name;
protected String surname;
...getters and setters...
}
我想通过姓名或姓氏订购人员集合。其实我只是这样做:
Collections.sort(listofpersons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) { return p1.getName().compareTo(p2.getName()); }
})
或
Collections.sort(listofpersons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) { return p1.getSurname().compareTo(p2.getSurname()); }
})
我试图实现通用比较器,例如:
MyUtils.sort(listofpersons,"getName");
MyUtils.sort(listofpersons,"getSurame");
我试图理解用仿制药和反思来做到这一点,但是我被困住了。我这样做:
public static <T> void sortCollection(Collection<T> list, final String methodName) {
Comparator<T> comparator = new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
try {
String a=o1.getClass().getMethod(methodName).invoke(o1).toString();
String b=o2.getClass().getMethod(methodName).invoke(o2).toString();
return a.compareTo(b);
} catch (Exception ex) {
...log somewhere...
return 0;
}
}
};
Collections.sort(list, comparator);
}
忘记了捕获异常和现在的String转换,重点是Collections.sort(Collection,Comparator)不存在,但我不知道创建一个Comparator ... 我非常好奇地知道我的想法是否有任何意义(原因)以及实施它的正确方法。
谢谢!
答案 0 :(得分:1)
问题在于sortCollection
方法的签名。您应该将list
参数的类型从Collection<T>
更改为List<T>
。换句话说,您的方法标题应为:
public static <T> void sortCollection(List<T> list, final String methodName)
这是因为Collections.sort()
期望其第一个参数为List
(参见其javadoc)
编辑:
关于您的实现,您可以使用一些通用/反射技巧。这是一个可能的替代方案。
@SuppressWarnings({ "unchecked", "rawtypes" })
public static<T> Comparator<T>
newMethodComparator(Class<T> cls, String methodName) throws Exception {
Method method = cls.getMethod(methodName);
if (method.getParameterTypes().length != 0)
throw new Exception("Method " + method + " takes parameters");
Class<?> returnType = method.getReturnType();
if (!Comparable.class.isAssignableFrom(returnType))
throw new Exception("The return type " + returnType + " is not Comparable");
return newMethodComparator(method, (Class<? extends Comparable>) returnType);
}
private static<T,R extends Comparable<R>> Comparator<T> newMethodComparator(
final Method method, final Class<R> returnType) throws Exception {
return new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
try {
R a = invoke(method, o1);
R b = invoke(method, o2);
return a.compareTo(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private R invoke(Method method, T o) throws Exception {
return returnType.cast(method.invoke(o));
}
};
}
以下是您将如何使用它:
List<Person> ps = new ArrayList<>(Arrays.asList(
new Person("A", "D"), new Person("B", "C")));
...
Comparator<Person> byNameComparator =
newMethodComparator(Person.class, "getName");
Collections.sort(ps, byNameComparator);
理由:
(a)这个impl。将适用于任何返回Comparable
对象的方法。你的实现是使用字符串比较,如果方法返回一个int,那么它将无法正常工作。
(b)这个impl。检查方法的返回类型以及它在创建Comparator
时采用零参数的事实,即在排序开始之前。因此,Collections.sort()
抛出异常的可能性较小。
(c)请注意@SuppressWarnings({ "unchecked", "rawtypes" })
不构成任何实际风险。如果方法的返回类型为Comparable
,那么向下转换将成功。如果返回类型不是Comparable
,那么Comparable.class.isAssignableFrom(returnType))
将返回false,该方法将抛出一个显式异常,并且永远不会达到向下转发。
答案 1 :(得分:0)
我倾向于说你可能最好只是创建不同比较器的具体实现,类似于你在问题开头所展示的内容。
public class PersonNameComparator implements Comparator<Person>
{
public int compareTo(Person o1, Person o2)
{
return o1.getName().compareTo(o2.getName());
}
}
public class PersonSurnameComparator implements Comparator<Person>
{
public int compareTo(Person o1, Person o2)
{
return o1.getSurname().compareTo(o2.getSurname());
}
}
然后只需调用标准库集合排序方法
Collections.sort(listofpersons, new PersonNameComparator());
Collections.sort(listofpersons, new PersonSurameComparator());
它可能有点代码味道,因为它会导致IDE生成大量的圆形板代码,但它至少具有易于编写一些单元测试的优点,并且比使用反射更具性能(如果只是略微)。
这种方法的缺点是每次向Person类添加一个新方法时都必须实现一个新的比较器,所以这里有很多膨胀。
答案 2 :(得分:0)
您无法对集合进行排序,只能对列表进行排序。这是因为并非所有集合(例如Set)都支持元素&#34; order&#34;的概念。
将方法签名更改为:
public static <T> void sort(List<T> list, final String methodName) {