【问题标题】:Sorting logic configured by string order按字符串顺序配置的排序逻辑
【发布时间】:2016-02-11 18:58:21
【问题描述】:

我有一个逗号分隔的字符串,其中包含对学生列表进行排序的排序标准:

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

上面的每个元素都是学生对象上的比较器,它们使用一些复杂的逻辑对学生对象进行排序。我需要知道如何最好地翻译出现在 sortOrder 字符串中的排序顺序,并按该顺序激活匹配的比较器。所以对于上面的例子,身高比较器会先运行,年龄最后运行。

【问题讨论】:

  • “所以对于上面的例子,身高比较器会先运行,年龄最后运行。”。排序不是这样工作的。你按身高排序。当高度相等时,您按重量排序。当权重相等时,按年龄排序。
  • 请澄清:您是否想要一个具有三个(或任何数量)级别的单一排序,如@GilbertLeBlanc 所述?或者三种不同的排序,按照String中列出的顺序执行?
  • @ErickG.Hagstrom 我正在寻找后者。
  • 这是您一天内就同一件事提出的第三个问题。您应该开始专注于在一个问题中解释您实际想要做什么,而不是打开新问题。请删除其他问题,然后编辑此问题,添加有关您的实际问题的更多信息。

标签: java sorting java-8 comparator


【解决方案1】:

您可以创建一个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));

【讨论】:

  • 这行不通。您将得到按输入字符串中最后一个字段排序的列表。相反,您应该使用 thenComparing 组合所有比较器:Comparator&lt;Student&gt; comp = Pattern.compile(", ") .splitAsStream(sortOrder) .map(comparators::get) .reduce(Comparator::thenComparing) .get();
  • 对与错,@Misha。这个答案是行不通的,正确的。但这不仅仅是一个简单的排序问题,尽管有标题。请参阅 cmets 关于今天 OP 提出的问题和其他问题。令人困惑。
  • @Misha OP 在 cmets 中提到,这正是他想要的,3 种不同的类型一个接一个。他可能想在重新排序之前对列表进行处理,例如打印它。作为一个问题,我正在写一个行为更合理的Comparator,直到我看到了OP的cmets然后写了这个。
  • 由于sort 是稳定的,使用比较器按特定顺序排序相当于使用比较器以相反顺序组合到一个的比较器排序一次……
  • @ErickG.Hagstrom 我认为当他说他正在寻找“后者”时,OP 很可能不理解这个问题。我看了他的其他问题,我同意,目前还不清楚他想要什么。
【解决方案2】:

如果我正确理解你想要什么,你想按身高、体重和年龄对学生列表进行排序。但是您希望这个属性列表是动态的。

这意味着我们需要实现一个自定义Comparator,它适用于给定类的给定属性。

第一个实现可能是创建一个Map,其中每个字符串属性都映射到一个关联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()
             );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-16
    • 1970-01-01
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多