Java 8 Stream 多个字段排序的大坑

1. 自定义类

public static class Person {
        private String name;
        private Integer age;
        private Integer level;
        
        public Person() {
        }
        public Person(String name, Integer age, Integer level) {
            this.name = name;
            this.age = age;
            this.level = level;
        }
		// 此处省略 get、set
    }

2. 单个字段排序

降序 reversed() (默认是升序)

直接排序如果排序字段为null的话会出现NullPointerException所以需要排空

解决方式:

  1. 过滤掉该字段为null的数据
  2. 使用nullsFirst()或者/nullsLast()
public class TestMain {
    public static void main(String[] args) {
        Person xm = new Person("小明", 18, 1);
        Person xh = new Person("小红", 22, 2);
        Person xb = new Person("小白", 18, 2);
        Person xl = new Person("小蓝", 22, 1);

        List<Person> list = Arrays.asList(xm, xh, xb, xl);

        // 以年龄降序 reversed() (默认是升序)
        List<Person> collect = list.stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.nullsFirst(Integer::compareTo)).reversed())
                .collect(Collectors.toList());

        for (Person person : collect) {
            System.out.println(JSONObject.toJSONString(person));
        }
        /* 结果
        {"age":22,"level":2,"name":"小红"}
        {"age":22,"level":1,"name":"小蓝"}
        {"age":18,"level":1,"name":"小明"}
        {"age":18,"level":2,"name":"小白"}
         */
    }
}

3. 多个字段排序

先以年龄 age 进行降序, 如果年龄相同则以层级 level 进行降序。

这里要注意,主排序字段要放到最后,次排序字段要放在前面

比如这里 level 字段要放前面, age 字段要放后面

// 多个字段进行降序, 先以年龄进行降序, 如果年龄相同则以层级进行降序
List<Person> collect = list.stream()
        .sorted(Comparator.comparing(Person::getLevel).reversed())
        .sorted(Comparator.comparing(Person::getAge).reversed())
        .collect(Collectors.toList());

/* 结果

{"age":22,"level":2,"name":"小红"}
{"age":22,"level":1,"name":"小蓝"}
{"age":18,"level":2,"name":"小白"}
{"age":18,"level":1,"name":"小明"}

 */
<think>嗯,我现在需要在Java 8中按照多个字段对List进行倒序排序。我之前学过一些Java 8的lambda表达式和Stream API,但多字段排序太清楚。首先,我记得单字段排序可以用Comparator.comparing,然后加上reversed()来倒序。但多个字段应该怎么处理呢? 可能需要使用thenComparing方法,这样可以在主排序条件之后添加次要条件。例如,先按年龄排序,再按姓名排序过用户想要的是倒序,可能每个字段都需要单独指定倒序?或者整个排序都是倒序? 比如有一个Person类,有name和age字段,用户希望先按age倒序,再按name倒序。这时候应该怎么组合Comparator呢?或者用户可能希望先按某个字段倒序,再按另一个字段正序?需要明确需求,但示例应该覆盖这两种情况。 首先,创建Comparator的时候,使用Comparator.comparing(Person::getAge).reversed()来对年龄倒序。然后,用thenComparing来添加第二个字段,同样加上reversed()如果需要。例如: Comparator<Person> comparator = Comparator .comparing(Person::getAge).reversed() .thenComparing(Person::getName).reversed(); 过这样可能有问题,因为第二个reversed()会把前面的整个比较器都反转吗?还是只反转当前的thenComparing部分?可能需要分开处理每个字段排序方向。 正确的做法应该是每个字段单独指定是否倒序。比如: Comparator<Person> comparator = Comparator .comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName, Comparator.reverseOrder()); 这样每个字段都单独设置倒序。或者使用reversed()方法,但需要注意链式调用的顺序。 另外,Java 8的Comparator.comparing方法可以接受一个Comparator作为第二个参数,这样就可以直接指定排序顺序。例如,Comparator.reverseOrder()作为参数传递进去。 然后,在排序的时候,使用stream().sorted(comparator).collect(Collectors.toList()),或者直接使用List的sort方法。 可能需要一个具体的例子来说明。比如,有一个Person列表,先按age倒序,如果age相同,再按name倒序。这时候的Comparator应该是: Comparator<Person> comparator = Comparator .comparing(Person::getAge).reversed() .thenComparing(Person::getName).reversed(); 但这样是否正确呢?或者需要分别对每个字段应用reversed? 或者应该先正序,再整体反转?过这样可能达到每个字段单独倒序的效果。 可能正确的做法是,对每个字段单独设置Comparator.reverseOrder()。例如: Comparator<Person> comparator = Comparator .comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName, Comparator.reverseOrder()); 这样每个字段都独立地倒序排列。这样写可能更清晰,避免链式调用reversed()带来的混淆。 接下来,需要写示例代码。假设Person类有age和name两个字段,那么示例代码可能是这样的: List<Person> sortedList = list.stream() .sorted(Comparator.comparing(Person::getAge).reversed() .thenComparing(Person::getName).reversed()) .collect(Collectors.toList()); 或者: list.sort(Comparator.comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName, Comparator.reverseOrder())); 这两种写法哪种更正确呢?或者两种都可以?需要测试一下。 假设用户的排序需求是先按age倒序,再按name倒序。那么两种写法都应该正确。但要注意的是,如果使用thenComparing后接reversed(),是否会影响到前面的排序条件?例如: 假设先比较age倒序,然后thenComparing比较name正序,再reversed(),这样name也会倒序。但如果是分开在每个comparing后面加reversed,可能会更直观。 可能更好的做法是,对每个字段单独指定排序顺序,使用Comparator.comparing(Function, Comparator)这个重载方法,这样更明确。 比如: Comparator.comparing(Person::getAge, Comparator.reverseOrder()) 这样明确指定age字段倒序,然后在thenComparing中同样处理name字段。 因此,完整的示例代码可能是: import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Person> list = ...; // 初始化列表 List<Person> sortedList = list.stream() .sorted(Comparator.comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName, Comparator.reverseOrder())) .collect(Collectors.toList()); } } class Person { private String name; private int age; // getters and setters } 或者使用List的sort方法: list.sort(Comparator.comparing(Person::getAge).reversed() .thenComparing(Comparator.comparing(Person::getName).reversed())); 但这里需要注意,thenComparing的参数应该是一个Comparator,所以第二种写法可能需要调整: .thenComparing( person -> person.getName(), Comparator.reverseOrder() ) 或者更简洁的写法: .thenComparing(Person::getName, Comparator.reverseOrder()) 所以综合来看,使用Comparator.comparing方法并传递Comparator.reverseOrder()作为第二个参数,会更加清晰,避免链式调用带来的混淆。 另外,如果用户需要混合排序,比如先按age倒序,再按name正序,可以单独为每个字段指定顺序: Comparator.comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName) 这样,age倒序,name正序。 总结,处理多字段倒序排序的关键是使用Comparator的comparing和thenComparing方法,并为每个字段指定相应的排序顺序,可以通过传递Comparator.reverseOrder()作为参数,或者在每个比较器后调用reversed()方法。 另外,需要注意引用中的示例,引用[2]提到的是按某个字段倒序排序,使用Comparator.comparing(Person::getAge).reversed(),这可能是在单字段的情况。扩展到多字段的话,需要继续添加thenComparing部分,并同样应用reversed()或者传递reverseOrder()。 现在,用户的问题是如何在Java 8中对List按多个字段进行倒序排序,所以示例需要展示两个或多个字段的倒序排序方法。 例如,先按age倒序,再按name倒序。代码示例应该体现这一点,并且正确使用Comparator的组合方式。 此外,可能需要提到,如果实体类中的字段是基本类型或其包装类,那么可以直接使用Comparator.comparing;如果是自定义类型,可能需要提供自定义的比较器。 最后,需要确保代码示例中的语法正确,比如使用正确的lambda表达式和方法引用。</think>在 Java 8 中,可以通过组合多个 `Comparator` 实现按多字段倒序排序。以下是分步说明和示例: --- ### 步骤说明 1. **单字段倒序排序**:使用 `Comparator.comparing(字段).reversed()` 或 `Comparator.comparing(字段, Comparator.reverseOrder())`[^2]。 2. **多字段排序**:通过 `thenComparing` 方法依次添加次要排序条件,并为每个字段单独指定倒序规则。 3. **链式调用**:将主排序字段和次要排序字段的规则组合成一个完整的 `Comparator`。 --- ### 示例代码 假设有一个 `Person` 类,需按 `age` 倒序排序,若 `age` 相同则按 `name` 倒序排序: ```java import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Person> persons = List.of( new Person("Alice", 30), new Person("Bob", 25), new Person("Charlie", 30) ); // 多字段倒序排序:先按 age 倒序,再按 name 倒序 Comparator<Person> comparator = Comparator .comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName, Comparator.reverseOrder()); List<Person> sortedList = persons.stream() .sorted(comparator) .collect(Collectors.toList()); sortedList.forEach(System.out::println); } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } ``` #### 输出 ``` Person{name='Charlie', age=30} Person{name='Alice', age=30} Person{name='Bob', age=25} ``` --- ### 混合排序场景 若需 **主字段倒序,次字段正序**(例如先按 `age` 倒序,再按 `name` 正序): ```java Comparator<Person> mixedComparator = Comparator .comparing(Person::getAge, Comparator.reverseOrder()) .thenComparing(Person::getName); // 默认正序 ``` --- ### 关键点 1. **灵活组合**:通过 `thenComparing` 可无限扩展次要排序字段。 2. **明确指定排序方向**:推荐使用 `Comparator.comparing(字段, Comparator.reverseOrder())`,避免链式 `reversed()` 的歧义。 3. **方法引用与Lambda**:优先使用方法引用(如 `Person::getAge`),使代码更简洁[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值