【问题标题】:How to use Comparator with conditions in java如何在java中使用带有条件的比较器
【发布时间】:2019-07-25 07:21:43
【问题描述】:

我正在尝试根据从 API 收到的排序键和排序顺序对列表进行排序。 例如, 我有一个带有 sortkey 和 sortorder 的列表,我需要根据它进行排序。

List<SortList> sortlist;

我有一个对象列表:

List<Employee> employee;

我可以使用

进行排序
Collections.sort(sourceList, Comparator
                .comparing(Employee::getAge).reversed()
                .thenComparing(Employee::getCount));

但我需要在一个条件下检查 sortfeild,并基于该条件仅考虑对该字段进行排序。

例如:

如果(排序键 = “名称”) 按排序顺序从 sortlist 中 sortbythatkey

if (sortkey = "place") sortbythat key 从 sortlist 按排序顺序

所以这里如果 sortlist 有名称和位置,那么它应该按键和顺序排序

知道我怎么能做到这一点吗?

排序列表包含:

{
    "sortKey":"name",
    "sortOrder":"ASC"

},
{
    "sortKey":"place",
    "sortOrder":"DESC"

}

要求是像 SQL 中的 ORDER BY 一样将它们链接在一起

【问题讨论】:

  • 如果两个元素有不同的sortkeys,你需要它如何表现?
  • @Marv :实际上排序键和顺序将由外部提供。所以应该根据 sortkeys 和排序顺序链接排序
  • 你能举例说明sortlist 可能包含的内容吗?
  • @Sweeper 添加......

标签: java java-8 comparator


【解决方案1】:

假设sortlistSortCriteria的一个列表,就是这样一个类:

class SortCritera {
    private String key;
    private String order;

    public String getKey() {
        return key;
    }

    public String getOrder() {
        return order;
    }

    // constructors, setters...
}

您首先需要一个HashMap&lt;String, Comparator&lt;Employee&gt;&gt; 来存储每个可能键的所有对应比较器:

HashMap<String, Comparator<Employee>> comparators = new HashMap<>();
comparators.put("name", Comparator.comparing(Employee::getName));
comparators.put("age", Comparator.comparing(Employee::getAge));
// ...

然后你可以遍历sortlist并继续调用thenComparing

Comparator<Employee> comparator = comparators.get(sortlist.get(0).getKey());
if (sortlist.get(0).getOrder().equals("DESC")) {
    comparator = comparator.reversed();
}
for(int i = 1 ; i < sortlist.size() ; i++) {
    if (sortlist.get(i).getOrder().equals("DESC")) {
        comparator = comparator.thenComparing(comparators.get(sortlist.get(i).getKey()).reversed());
    } else {
        comparator = comparator.thenComparing(comparators.get(sortlist.get(i).getKey()));
    }
}
// now you can sort with "comparator".

正如 Holger 所建议的,您也可以使用 Stream API 来执行此操作:

sortlist.stream().map(sc -> {
    Comparator<Employee> c = comparators.get(sc.getKey()); 
    return sc.getOrder().equals("DESC")? c.reversed(): c; 
}).reduce(Comparator::thenComparing)
.ifPresent(x -> Collections.sort(originalList, x));

【讨论】:

  • 为什么第一个 get(0) 情况出现在 for 循环之外?它不检查 ASC 案例对吗?
  • @KarthikCherukunnumal 第一种情况比较特殊,因为您需要调用comparing 而不是thenComparing。我假设如果它不是下降的,它就是上升的。
  • 好的,但是对于第二种情况,我们将与 Comparator.comparing(Employee::getName) 进行比较,对吗?这行得通吗?
  • 循环体内有很多代码重复。另一个局部变量呢? Comparator&lt;Employee&gt; next = comparators.get(sortlist.get(i).getKey()); if(sortlist.get(i).getOrder().equals("DESC")) { next = comparator.reversed(); } comparator = comparator.thenComparing(next); 或者您使用带有 Reduction 的 Stream API:sortlist.stream() .map(sc -&gt; { Comparator&lt;Employee&gt; c = comparators.get(sc.getKey()); return sc.getOrder().equals("DESC")? c.reversed(): c; }) .reduce(Comparator::thenComparing) .ifPresent(theList::sort);
  • 也可以用x -&gt; originalList.sort(x)代替x -&gt; Collections.sort(originalList, x),相当于originalList::sort...
【解决方案2】:

您可以创建一个方法,在传递排序键时提供适当的比较器:

public Comparator<Employee> getComparator(String sortKey) {
    if("name".equals(sortKey)) {
        return Comparator.comparing(Employee::getName);
    } else if ("place".equals(sortKey) {
        return Comparator.comparing(Employee::getPlace);
    } else {
        throw new IllegalArgumentException();
    }
}

简单来说就是:

Collections.sort(sourceList, getComparator(sortKey).reversed()
                .thenComparing(Employee::getCount));

虽然您也可以自己编写,但我发现最好委派“标准”部分并简单地编写与此不同的部分。

如果您发现自己有很多这样的排序键,那么更合适的方法是使用地图:

private static final Map<String, Comparator<Employee>> COMPARE_MAP = new HashMap<>() {{
    put.("name", Comparator.comparing(Employee::getName));
    put.("place", Comparator.comparing(Employee::getPlace));
}});

public Comparator<Employee> getComparator(String sortKey) {
    if(COMPARE_MAP.containsKey(sortKey)) {
        return COMPARE_MAP.get(sortKey);
    } else {
        throw new IllegalArgumentException();
    }
}

反射也是一种选择,但我会谨慎使用反射,除非它变得不切实际。在这种情况下,您可以创建自己的注释来确定 Employee 类的哪些字段可用于排序。

【讨论】:

  • 这里如何确定排序顺序,request中的排序顺序可能包含DESC和ASC,那么如何以此为基础进行排序呢?它也会像将它们链接在一起一样工作吗?
  • 在这类事情上使用Map 更简洁。
  • @chrylis 没错,事实上,如果我必须在这里再添加一个,我肯定会使用地图。我会相应地调整我的答案。
  • @Neil :在这里我如何添加 sortOrder,因为我还检查需要检查 sortOrder 并相应地进行排序
  • 使用Map 是一种干净的方法,创建HashMap 的子类,只是为了在源代码中保存一些字符,不是。此外,put.( 甚至不是有效的语法。
猜你喜欢
  • 1970-01-01
  • 2019-01-13
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 2015-10-05
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
相关资源
最近更新 更多