【问题标题】:Stream sorting issue流排序问题
【发布时间】:2026-02-02 07:30:01
【问题描述】:

我有一个项目列表(MyDetail 对象),我想使用流排序方法对其进行排序。该对象有 3 个字段:field1、field2、field3。 我想先按 field3 然后按 field2 然后按 field1 排序,所有这些都是相反的顺序。 所以我写了一个方法sortMyList。

我有一个未排序项目 unSortedDetails 的列表,如下所示: myDetail1:“20180201”,假,假 myDetail2:“20180101”,假,假 myDetail3: "20180101", false, true

在 sortMyList(unSortedDetails) 之后,我希望我的结果是 myDetail3、myDetail1、myDetail2,但实际结果是 myDetail1、myDetail3、myDetail2,为什么?

所以如果我像下面这样为 MyDetail 实现 Comparable,那么它会按预期工作。这太奇怪了。我不知道为什么。谢谢你的帮助!

public List<MyDetail> sortMyList(List<MyDetail> unSortedDetails){
    List<MyDetail> myDetails = unSortedDetails
                        .stream().sorted(Comparator.comparing(MyDetail::getField11).reversed()
                                .thenComparing(MyDetail::getField2).reversed()
                                .thenComparing(MyDetail::getField3).reversed())
                        .collect(Collectors.toList());
                        return myDetails;
                        }

                        @Setter
                        @Getter
                        public class MyDetail{
                            String field1;
                            Boolean field2; 
                            Boolean field3; 
                        }



                @Setter
                @Getter
                public class MyDetail implement Comparable<MyDetail>{
                    String field1;
                    Boolean field2; 
                    Boolean field3; 

                        @Override
                        public int compareTo(MyDetail o) {
                            if (this == o || this.equals(o)) return 0;
                            if (field3) return -1;
                            if (o.field3) return 1;
                            if (!field3 && !o.field3 && field2) return -1;
                            if(!field3 && !o.field3 &&!field2 && o.field2) return 1;
                            if(!field3 && !o.field3
                                    &&!field2 && !o.field2){
                                return o.field1.compareTo(field1);
                            }
                            return 0;
}
                }

【问题讨论】:

  • 您的 field* 字段属于 String 类型,但您试图在 if 语句中将它们视为 boolean 条件。
  • 哦,对不起,field3, field2 是布尔值,field1 是字符串,已更新

标签: java sorting java-8 java-stream


【解决方案1】:

您的 Comparator 几乎没有问题。首先是每次您调用reversed() 时,您都在反转比较器的所有 之前的设置。

所以你的比较器代表(参见 cmets 中的步骤,FieldX 减少到 Fx

Comparator.comparing(MyDetail::getField11)     //  F1 ASC
          .reversed()                          //~(F1 ASC) 
                                               //  F1 DESC 
          .thenComparing(MyDetail::getField2)  //  F1 DESC, F2 ASC  
          .reversed()                          //~(F1 DESC, F2 ASC) 
                                               //  F1 ASC,  F2 DESC
          .thenComparing(MyDetail::getField3)  //  F1 ASC,  F2 DESC, F3 ASC
          .reversed()                          //~(F1 ASC,  F2 DESC, F3 ASC)
                                               //  F1 DESC, F2 ASC,  F3 DESC

所以你最终得到了订单Field1 DESC, Field2 ASC, Field3 DESC

要为每个字段指定相反的顺序,请传递该字段的已反转比较器,例如.thenComparing(Comparator.comparing(YourClass::getField).reversed())


下一个问题是 Comparator 使用的字段的顺序。在你的问题中你说:

我想先按 field3 然后按 field2 然后按 field1 排序

但您的比较器首先检查字段 1,然后是字段 2,然后是字段 3(因为您按此顺序通过 .thenComparing 添加它们)。

你的代码应该更像

Comparator.comparing(MyDetail::getField13).reversed()
          .thenComparing(Comparator.comparing(MyDetail::getField2).reversed())
          .thenComparing(Comparator.comparing(MyDetail::getField1).reversed())

所以你正在创建~(F3 ASC), ~(F2 ASC), ~(F1 ASC),结果是F3 DESC, F2 DESC, F1 DESC


顺便说一句,pointed by Holger 你可以通过

达到同样的效果
Comparator.comparing(MyDetail::getField3)
          .thenComparing(MyDetail::getField2) 
          .thenComparing(MyDetail::getField1)
          .reversed()

请注意,Comparaor.comparing(FunctionToValue)thenComparing(FunctionToValue) 将为选定的值创建 ASCending 比较器。

所以前 3 行构造了 Comparator 描述顺序 F3 ASC, F2 ASC, F1 ASC。反转之后
~(F3 ASC, F2 ASC, F1 ASC)也给了我们想要的F3 DESC, F2 DESC, F1 DESC

【讨论】:

  • 非常感谢!你的解释很清楚。喜欢它!
  • 还有一个问题,那么流排序与类可比,哪种方式更快?我会投票后者,但我测试并没有看到两者之间有太大区别。
  • 我现在帮不了你,但这听起来是个有趣的问题。考虑为其创建单独的帖子并包含您的测试。无论如何,我一开始可能不会被性能所困扰,因为Comparable 接口的目的是为元素提供 default 顺序,而Comparator 的目的是让其他人使用任何 custom下订单。我们通常不需要考虑默认顺序与用户定义顺序的性能,但就像我说的,这可能是其他人回答的有趣问题。
  • 你把事情复杂化了。要为所有属性使用反向顺序获取比较器,只需为所有属性创建一个比较器并反转最终结果:Comparator.comparing(MyDetail::getField3) .thenComparing(MyDetail::getField2) .thenComparing(MyDetail::getField1).reversed()
  • @Holger 没错,我主要专注于解释问题(reversed 的滥用和错误的字段顺序)。但是,虽然您建议的方法是有效的,但使用 thenComparing(reversedComparator) 的 IMO 更容易遵循,因此也不易出错。无论如何感谢您指出,将您的变体添加到答案中。