按字符串顺序

时间:2015-11-10 19:15:34

标签: java sorting java-8 comparator

我有一个逗号分隔的字符串,其中包含排序学生列表的排序条件:

Input:
String sortOrder = "height, weight, age"
List<student> students;

上面的每个元素都是学生对象上的Comparators,它们使用一些复杂的逻辑对Student对象进行排序。我需要知道如何最好地翻译sortOrder字符串中出现的排序顺序,并按顺序激活匹配的比较器。因此,对于上面的示例,高度比较器将首先运行,并且最后一个运行。

2 个答案:

答案 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()
             );
相关问题