如何缩短具有多个字段的比较器?

时间:2016-05-10 08:29:49

标签: java reflection lambda comparator

我正在尝试在java8中编写一个Comparator,它依赖于使用lambda的两个标准。我有一个List人。 Person有以下方法:

Person{
   String getFirstName();
   String getLastName();
   int getHeight();
   Date getBirthday();
}

列表的排序取决于两个可以随意选择的标准。所以可能是列表应该是firstname和birthday,或者是firstname和height等等。

我的方法是创建一个switch-case块,我在其中查看不同的条件组合。但是这种方法变得太大了。

switch (holder.criteria1) {
        case FIRSTNAME:
            switch (holder.criteria2) {
                case FIRSTNAME:
                    list.sort(Comparator.comparing(Person::getFirstName,
                            Comparator.nullsFirst(String::compareTo)));
                    break;
                case LASTNAME:
                    list.sort(Comparator.comparing(Person::getFirstname,
                            Comparator.nullsFirst(String::compareTo)).thenComparing(
                            Person::getLastName, Comparator.nullsFirst(String::compareTo)));
                    break;
                case HEIGHT:
                    list.sort(Comparator.comparing(Person::getFirstname,
                            Comparator.nullsFirst(String::compareTo)).thenComparing(
                            Person::getHeight, Comparator.nullsFirst(Integer::compareTo)));
                    break;
                case BIRTHDAY:
                    list.sort(Comparator.comparing(Person::getFirstname,
                            Comparator.nullsFirst(String::compareTo)).thenComparing(
                            Person::getBirthday, Comparator.nullsFirst(Date::compareTo)));
                    break;
            }
            break;

我必须为每个案例组合重复这个。条件2的四个案例中有三个几乎相同,只有方法名称和类型发生变化。这是一个非常丑陋和冗长的代码,我想以更好的方式重新设计它。

有没有办法通过使用反射缩短这个?

3 个答案:

答案 0 :(得分:3)

也许你可以这样做:

public enum Holder{
 FIRSTNAME{
   @override
    Function getFunction(){
     return Person::getFirstName;
    }
}

 ........
 abstract Function getFunction();
}

然后你只需要这个电话:

list.sort(Comparator.comparing(holder1.getFunction(),
                          Comparator.nullsFirst(String::compareTo)).thenComparing(
                        holder2.getFunction(), Comparator.nullsFirst(String::compareTo)));

答案 1 :(得分:3)

您可以将比较器存储在枚举值中,然后按需组合:

$self->routes

答案 2 :(得分:1)

作为Jorn Vernee’s answer的附录,这里的派生变体避免了代码重复:

enum SortOn {
    FIRSTNAME(Person::getFirstName),
    LASTNAME(Person::getLastName),
    HEIGHT(Person::getHeight),
    BIRTHDAY(Person::getBirthday);

    public final Comparator<Person> comparator;

    private <U extends Comparable<U>> SortOn(Function<Person,U> f) {
        this.comparator = Comparator.comparing(f,
            Comparator.nullsFirst(Comparator.naturalOrder()));
    }
    private SortOn(ToIntFunction<Person> f) {
        this.comparator = Comparator.comparingInt(f);
    }
}

由于所有属性都具有Comparable类型,因此当需要自然顺序时,可以对它们进行类似处理。

虽然它们都可以正常工作,但HEIGHT实例在此处使用替代构造函数来创建特定于Comparator属性的int,以避免装箱开销。由于这些值永远不会是null,因此对于此特定属性,null - 检查也已过时。

这个枚举仍然可以使用相同的方式,例如

public static void sort(List<Person> list, SortOn criteria1, SortOn criteria2) {
    if(criteria1 == criteria2) {
        list.sort(criteria1.comparator);
    } else {
        list.sort(criteria1.comparator.thenComparing(criteria2.comparator));
    }
}

或支持任意数量的标准:

public static void sort(List<Person> list, SortOn... criteria) {
    list.sort(Arrays.stream(criteria).map(c -> c.comparator)
        .reduce(Comparator::thenComparing)
        .orElseThrow(() -> new IllegalArgumentException("no criteria given")));
}