如何缩短多字段比较器?

pkbketx9  于 2021-07-03  发布在  Java
关注(0)|答案(3)|浏览(366)

我试图用java8编写一个比较器,它依赖于使用lambda的两个标准。我有一个 List 人。一 Person 有以下方法:

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

列表的排序取决于两个标准,这两个标准可以由遗嘱选择。所以这个列表应该按照名字和生日,或者名字和身高等等来排序。
我的方法是创建一个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的四分之三几乎相同,只是方法名和类型发生了变化。这是一个非常丑陋和漫长的代码,我想重新设计一个更好的方式。
有没有一种方法可以通过使用反射来缩短这个时间?

62o28rlo

62o28rlo1#

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

enum SortOn {
    FIRSTNAME(Comparator.comparing(Person::getFirstName,
        Comparator.nullsFirst(String::compareTo))),
    LASTNAME(Comparator.comparing(Person::getLastName, 
        Comparator.nullsFirst(String::compareTo))),
    HEIGHT(Comparator.comparing(Person::getHeight, 
        Comparator.nullsFirst(Integer::compareTo))),
    BIRTHDAY(Comparator.comparing(Person::getBirthday, 
        Comparator.nullsFirst(Date::compareTo)));

    public final Comparator<Person> comparator;

    private SortOn(Comparator<Person> comparator) {
        this.comparator = comparator;
    }
}
...
public void sort(SortOn criteria1, SortOn criteria2) {
    if(criteria1 == criteria2) {
        list.sort(criteria1.comparator);
    } else {
        list.sort(criteria1.comparator.thenComparing(criteria2.comparator));
    }
}
r7s23pms

r7s23pms2#

也许你可以这样做:

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)));
vnzz0bqm

vnzz0bqm3#

作为jorn vernee答案的附录,这里的派生变体避免了代码重复:

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")));
}

相关问题