java—如何按两个字段排序,其中一个字段是enum?

az31mfrm  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(392)

我需要按名称的字母顺序对列表进行排序,然后再按类型对其进行一次排序,并将具有特定类型的元素放在列表的顶部。这是我迄今为止所做的,但它没有像预期的那样工作,它只返回按名称排序的列表。

public List<PRDto> present(
      List<ParticipantReference> participants) {
    return participants.stream()
        .map(MAPPER::toDto)
        .sorted(Comparator.comparing(PRDto::getName)
            .thenComparing(PRDto::getParticipantType, (type1, type2) -> {
              if (type1.equals(type2)) {
                return 0;
              }
              if (type2.equals(ParticipantType.S.getDescription())) {
                return 1;
              }
              if (type1.equals(ParticipantType.S.getDescription())) {
                return -1;
              }
              return type1.compareTo(type2);
            }))
        .collect(toList());
    }

这是我的枚举:

@Getter
public enum ParticipantType {
  F("F"),
  D_F("D+F"),
  S("S");

  private final String description;

  ParticipantType(String description) {
    this.description = description;
  }
}
6mw9ycah

6mw9ycah1#

为了保持可读性,我不会将比较放在流管道中,而是在比较器中提取它

public List<PRDto> present(List<ParticipantReference> participants) {

    Comparator<PRDto> byType = Comparator.comparing(o -> !o.getType().equals(ParticipantType.S));

    Comparator<PRDto> byName = Comparator.comparing(PRDto::getName);

    return participants.stream().sorted(byType.thenComparing(byName)).collect(toList());
}

我的回答有多余的逻辑,霍尔格谢天谢地向我指出了这一点。这也可以内联,因为霍尔格在评论中提到:

return participants.stream().sorted(
                   Comparator.comparing((PRDto o) -> !o.getType().equals(ParticipantType.S))
                             .thenComparing(PRDto::getName))
            .collect(toList());
s5a0g9ez

s5a0g9ez2#

请注意,如果要先按类型排序,然后按名称排序,则需要这样做。反之,则需要排序算法来保持相等元素的现有顺序,这可能并不总是有保证的。
将枚举Map到表示order和let的整数也会更容易 Comparator 做这项工作。
所以你的代码可以是这样的:

...
.sorted(Comparator.comparingInt(p -> { //first compare by mapped type
     switch( p.getParticipantType() ) {
       case S: return 0;
       default: return Integer.MAX_VALUE; //so you could insert other enum values if needed
     }
   }).thenComparing(PRDto::getName) //then compare by name
)
...

如果您使用这样的复合比较器,它会将元素类型 S 在顶端。如果你把比较器写成“旧”的样子,它可能是这样的:

compare(PRDto left, PRDto right) {
  int typeOrderLeft = mapToInt(left.getParticipantType()); //mapToInt contains the mapping expression, i.e the switch in the example above
  int typeOrderRight = mapToInt(right.getParticipantType());

  //first order by type
  int result = Integer.compare(typeOrderLeft, typeOrderRight);

  //if the type order is the same, i.e. both have S or non-S, sort by name
  if( result == 0 ) { 
    result = String.compare(left.getName(), right.getName());
  }

  return result; 
}

示例(已订购):

type  name
---------------
S     zulu      //S < F and S < D_F due to the mapping, name is irrelevant here
D_F   alpha     //F and D_F are considered equal, so names are compared
F     bravo

相关问题