OrderByUtils

Posted by Pismery Liu on Sunday, December 15, 2019

TOC

Order by 操作

排序问题是日常开发中经常遇到的问题,以下是 Java 解决各类排序问题的一些例子,以供大家参考。

单字段排序

对于排序问题,Java 提供了两种解决方案一种是实现接口 Comparable 的 compareTo 方法,另一种是使用 Comparator 工具类;两者都能实现排序的功能。日常开发中,我更倾向使用工具类 Comparator 的方式,理由如下:

  1. 对于某一个对象,我们需要多种排序方式时,实现接口的方式则难以实现;而工具类 Comparator 可以提供不同的 Comparator,从而很好地实现这样的需求;
  2. Java 8 中通过 Lambda 引入了更多的 Api 使得使用 Comparator 代码更加易懂,简单。

单字段升序排序

本文所有示例,采用 Employee 类作为待排列表的元素,下面是 Employee 类的定义

@Data
@AllArgsConstructor
private static class Employee {
    private Integer id;
    private String name;
    private Integer age;
    private Position position;
}

private enum Position {
    BRAND_1, BRAND_2, BRAND_3, BRAND_4
}

首先,先通过根据 Employee 中 name 字段升序排序的例子,看看如何使用接口和工具类的方式实现;

实现接口 Comparable 方式

@Data
@AllArgsConstructor
private static class Employee implements Comparable<Employee>{
    private Integer id;
    private String name;
    private Integer age;
    private Position position;

    @Override
    public int compareTo(Employee employee) {
        return this.name.compareTo(employee.getName());
    }
}

@Test
public void orderBy_single_string_java7_asc() {
    //Given
    System.out.println("orderBy_single_string_comparable_asc");
    //When
    Collections.sort(singleList);

    singleList.forEach(System.out::println);
}

使用工具类 Comparator 方式

// Java 7 实现
@Test
public void orderBy_single_string_java7_asc() {
    //Given
    System.out.println("orderBy_single_string_java7_asc");
    //When

    Collections.sort(singleList, new Comparator<Employee>() {
        @Override
        public int compare(Employee o1, Employee o2) {
            return o1.getName().compareTo(o2.getName());
        }
    });

    singleList.forEach(System.out::println);
}
// Java 8 实现
@Test
public void orderBy_single_string_java8_asc() {
    //Given
    System.out.println("orderBy_single_string_java8_asc");
    //When
    singleList.sort(Comparator.comparing(Employee::getName));

    singleList.forEach(System.out::println);
}

运行结果

单字段降序排序

下面根据 name 字段进行降序排序,可以感受到 Lambda 使得代码更清晰易懂;

// Java 7 实现
@Test
public void orderBy_single_string_java7_desc() {
    //Given
    System.out.println("orderBy_single_string_java7_desc");
    //When
    Collections.sort(singleList, new Comparator<Employee>() {
        @Override
        public int compare(Employee o1, Employee o2) {
            return o2.getName().compareTo(o1.getName()); //o2 compare o1
        }
    });

    singleList.forEach(System.out::println);
}


// Java 8 实现
@Test
public void orderBy_single_string_java8_desc() {
    //Given
    System.out.println("orderBy_single_string_java8_desc");
    //When
    singleList.sort(Comparator.comparing(Employee::getName).reversed());

    singleList.forEach(System.out::println);
}

运行结果

根据枚举字段 position 排序

下面是根据枚举字段 position 进行降序排序,看看枚举类型是如何排序的。

Java 7

// Java 7 实现
@Test
public void orderBy_single_enum_java7_desc() {
    //Given
    System.out.println("orderBy_single_enum_java7_desc");
    //When
    Collections.sort(singleList, new Comparator<Employee>() {
        @Override
        public int compare(Employee o1, Employee o2) {
            return o2.getPosition().compareTo(o1.getPosition());
        }
    });

    singleList.forEach(System.out::println);
}

// Java 8 实现
@Test
public void orderBy_single_enum_java8_desc() {
    //Given
    System.out.println("orderBy_single_enum_java8_desc");
    //When
    singleList.sort(Comparator.comparing(Employee::getPosition).reversed());

    singleList.forEach(System.out::println);
}

运行结果

多字段排序

日常开发中,多字段的排序也十分常见,下面根据字段 name(asc), age(asc), position(desc) 进行排序,看看如何实现多字段的排序;

Java 7

@Test
public void orderBy_multiple_java7_desc() {
    //Given
    System.out.println("orderBy_multiple_java8_asc");
    //When
    Collections.sort(multipleList, new Comparator<Employee>() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    int result;
                    result = o1.getName().compareTo(o2.getName());
                    if (result != 0) {
                        return result;
                    }

                    result = o1.getAge().compareTo(o2.getAge());
                    if (result != 0) {
                        return result;
                    }

                    return o2.getPosition().compareTo(o1.getPosition());
                }
            }
    );

    multipleList.forEach(System.out::println);
}

Java 8

public class OrderUtils {
    @SafeVarargs
    public static <T> void orderBy(List<T> list, Comparator<T>... comparators) {
        if (comparators == null || comparators.length == 0) {
            throw new IllegalArgumentException("No comparator!...");
        }

        Comparator<T> combineComparator = comparators[0];
        for (int i = 1; i < comparators.length; i++) {
            combineComparator = combineComparator.thenComparing(comparators[i]);
        }
        list.sort(combineComparator);
    }
}


@Test
public void orderBy_multiple_java8_desc() {
    //Given
    System.out.println("orderBy_multiple_java8_asc");
    //When
    OrderUtils.orderBy(multipleList
            , Comparator.comparing(Employee::getName)
            , Comparator.comparing(Employee::getAge)
            , Comparator.comparing(Employee::getPosition).reversed()
    );

    multipleList.forEach(System.out::println);
}

运行结果

自定义排序

有时,我们可能还需要自定义排序规则,下面根据 name(asc), age(asc), position(customer) 排序;

字段 position 自定义顺序规则:Brand_2 > Brand_1 > Brand_4 > Brand_3

private static Integer customerPositionWeight(Employee employee) {
    switch (employee.getPosition()) {
        case BRAND_1: return 2;
        case BRAND_2: return 1;
        case BRAND_3: return 4;
        case BRAND_4: return 3;
        default: throw new IllegalArgumentException();
    }
}

java 7

@Test
public void orderBy_multiple_java7_customer() {
    //Given
    System.out.println("orderBy_multiple_java7_customer");
    //When
    Collections.sort(multipleList, new Comparator<Employee>() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    int result;
                    result = o1.getName().compareTo(o2.getName());
                    if (result != 0) {
                        return result;
                    }

                    result = o1.getAge().compareTo(o2.getAge());
                    if (result != 0) {
                        return result;
                    }

                    Integer o1Weight = customerPositionWeight(o1);
                    Integer o2Weight = customerPositionWeight(o2);
                    return o1Weight.compareTo(o2Weight);
                }
            }
    );

    multipleList.forEach(System.out::println);
}

Java 8

@Test
public void orderBy_multiple_java8_customer() {
    //Given
    System.out.println("orderBy_multiple_java8_customer");
    //When

    OrderUtils.orderBy(multipleList
            , Comparator.comparing(Employee::getName)
            , Comparator.comparing(Employee::getAge)
            , (o1, o2) -> {
                Integer o1Weight = customerPositionWeight(o1);
                Integer o2Weight = customerPositionWeight(o2);

                return o1Weight.compareTo(o2Weight);
            }
    );

    //Then
    multipleList.forEach(System.out::println);
}

运行结果