我有一个逗号分隔的字符串,其中包含排序学生列表的排序条件:
Input:
String sortOrder = "height, weight, age"
List<student> students;
上面的每个元素都是学生对象上的Comparators,它们使用一些复杂的逻辑对Student对象进行排序。我需要知道如何最好地翻译sortOrder字符串中出现的排序顺序,并按顺序激活匹配的比较器。因此,对于上面的示例,高度比较器将首先运行,并且最后一个运行。
答案 0 :(得分:2)
您可以HashMap
将这些字词映射到Comparator
个对象。然后使用比较器进行排序
Map<String, Comparator<student>> comparators = new HashMap<>();
将Comparator
个对象添加到comparators
之后:
comparators.put("height", Comparator.comparingDouble(student::getHeight));
如果您想连续执行不同的排序,只需浏览sortOrder
中的字词并应用例如。
for (String comp : sortOrder.split(", "))
Collections.sort(students, comparators.get(comp));
答案 1 :(得分:1)
如果我理解你想要的是什么,你想按照他们的身高,然后是他们的体重和年龄对学生名单进行排序。但是你希望这个属性列表是动态的。
这意味着我们需要实现一个适用于给定类的给定属性的自定义Comparator
。
第一个实现可能是创建Map
,其中每个String属性都映射到关联Comparator
。这将是一个干净的方法(请参阅@Manos Nikolaidis的答案,以及Misha对此实施的评论)。
使用一点反射可以实现真正的动态解决方案:首先检索具有给定类的给定名称的声明字段。它设置为可访问,因为此字段很可能是私有的。最后,返回Comparator
比较每个学生的该字段的值。这段代码盲目地假设目标属性实际上是Comparable
(否则,我们为什么要使用这个字段进行比较?)。
private static <U> Comparator<U> comparingProperty(String property, Class<U> clazz) {
try {
Field field = clazz.getDeclaredField(property);
field.setAccessible(true);
return Comparator.comparing(s -> {
try {
@SuppressWarnings("unchecked")
Comparable<Object> comparable = (Comparable<Object>) field.get(s);
return comparable;
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
});
} catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
}
一旦我们有了这个实用程序比较器,我们就可以轻松地对学生列表进行排序。通过拆分","
并修剪结果来创建排序属性流。然后,每个属性都映射到其对应的Comparator
,最后,通过将所有比较符与Comparator::thenComparing
组合来减少此流:
students.sort(Stream.of(sortOrder.split(","))
.map(String::trim)
.map(s -> comparingProperty(s, Student.class))
.reduce(Comparator::thenComparing)
.get()
);