有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

集合Java,带反射的列表排序

我希望允许按类中的每个字段进行排序,而不必编写switch/if语句。 我的想法是通过名称找到与给定字符串值匹配的字段,然后使用流API进行排序。IntelliJ尖叫着说我需要用try-catch来包围它,所以它看起来不那么整洁,但这并不重要,因为它不起作用

    private List<MyEntity> getSorted(List<MyEntity> list, SearchCriteria criteria) {
        Field sortByField = findFieldInListByName(getFieldList(MyEntity.class), criteria.getSortBy());
        return list.stream().sorted(Comparator.comparing(entity-> {
            try {
                return (MyEntity) sortByField.get(entity);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return entity;
        })).collect(Collectors.toList());
    }

在MyEntity类中,我添加了Comparable接口,但我不确定Compare()的主体中应该包含什么,因为我不想指定如何比较对象,因为它将根据所选排序进行更改

编辑:在下面添加了实体:

@Data
@Entity
@Table(name = "role_management", schema = "mdr")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode

public class MyEntity implements Comparable{

    @Id
    @Column(name = "uuid", unique = true, insertable = false, updatable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID uuid;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private UserEntity user;

    @Basic
    @NonNull
    @Column(name = "role")
    private String role;

    @Basic
    @Column(name = "action")
    @Enumerated(EnumType.STRING)
    private RoleAction action;

    @Basic
    @Column(name = "goal")
    @Enumerated(EnumType.STRING)
    private RoleGoal goal;

    @Column(name = "date")
    private LocalDateTime date;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "reporter_id", referencedColumnName = "uuid")
    private UserEntity reporter;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "authorizer_id", referencedColumnName = "uuid")
    private UserEntity authorizer;

    @Basic
    @Column(name = "ezd")
    private String ezd;

    @Basic
    @Column(name = "is_last")
    private boolean isMostRecent;

    @Override
    public int compareTo(Object o) {
        return 0;
    }
}

编辑2:基于@Sweeper解决方案的我的代码:

用户实体(可为空)

    @Override
    public int compareTo(UserEntity other) {
        if (other == null) {
            return 1;
        }
        return this.getMail().compareTo(other.getMail());
    }

比较国:

    public static Comparator getSortComparator(Field sortByField) {
        return Comparator.nullsLast(Comparator.comparing(entity -> {
            try {
                Object fieldValue = sortByField.get(entity);
                if (!(fieldValue instanceof Comparable<?>) && fieldValue != null) {
                    throw new IllegalArgumentException("...");
                }
                return (Comparable) fieldValue;
            } catch (IllegalAccessException e) {
                throw new MdrCommonException(e.getLocalizedMessage());
            }
        }));
    }

共 (2) 个答案

  1. # 1 楼答案

    您可以为任何类using reflection的任何字段自动创建比较器,但最好创建特定的比较器(将进行类型检查)

    您的实体是一个带有普通字段的普通类。那么,通常的Java排序机制应该可以完成这项工作:

    基本上,如果为每个字段定义一个比较器(甚至是实体中的深字段):

    public final static Comparator<MyEntity> ByField1 = comparing(MyEntity::getField1);
    public final static Comparator<MyEntity> ByField2 = comparing(MyEntity::getField2);
    public final static Comparator<MyEntity> ByField3 = comparing(MyEntity::getField3);
    public final static Comparator<MyEntity> ByDeep1 = comparing(a -> a.getField4().getDeep1());
    public final static Comparator<MyEntity> ByDeep2 = comparing(a -> a.getField4().getDeep2());
    public final static Comparator<MyEntity> ByDeep3 = comparing(a -> a.getField4().getDeep3());
    

    可以使用复杂的排序表达式进行排序:

    data.stream()
            .sorted(ByField2.reversed().thenComparing(ByDeep2))
            .forEach(System.out::println);
    

    一个完整的例子可以是

    public static void main(String[] args) {
    
        List<MyEntity> data =
                Stream.of("Row1", "Row2").flatMap(field1 ->
                Stream.of(101, 102).flatMap(field2 ->
                Stream.of(true, false).flatMap(field3 ->
                Stream.of("Row1", "Row2").flatMap(deep1 ->
                Stream.of(101, 102).flatMap(deep2 ->
                Stream.of(true, false).map(deep3 ->
                    new MyEntity(field1, field2, field3, new MyDeepField(deep1, deep2, deep3))))))))
                .collect(toList());
    
        data.stream()
                .sorted(ByField2.reversed().thenComparing(ByDeep2))
                .forEach(System.out::println);
    
    }
    
    @Getter
    @Setter
    @AllArgsConstructor
    static class MyDeepField {
        private String deep1;
        private Integer deep2;
        private Boolean deep3;
    }
    
    @Getter
    @Setter
    @AllArgsConstructor
    static class MyEntity {
        private String field1;
        private Integer field2;
        private Boolean field3;
        private MyDeepField field4;
    
        public final static Comparator<MyEntity> ByField1 = comparing(MyEntity::getField1);
        public final static Comparator<MyEntity> ByField2 = comparing(MyEntity::getField2);
        public final static Comparator<MyEntity> ByField3 = comparing(MyEntity::getField3);
        public final static Comparator<MyEntity> ByDeep1 = comparing(a -> a.getField4().getDeep1());
        public final static Comparator<MyEntity> ByDeep2 = comparing(a -> a.getField4().getDeep2());
        public final static Comparator<MyEntity> ByDeep3 = comparing(a -> a.getField4().getDeep3());
    
        @Override
        public String toString() {
            return "MyEntity{" +
                    "field1='" + field1 + '\'' +
                    ", field2=" + field2 +
                    ", field3=" + field3 +
                    ", deep1=" + field4.getDeep1() +
                    ", deep2=" + field4.getDeep2() +
                    ", deep3=" + field4.getDeep3() +
                    '}';
        }
    }
    

    有输出

    MyEntity{field1='Row1', field2=102, field3=true, deep1=Row1, deep2=101, deep3=true}
    MyEntity{field1='Row1', field2=102, field3=true, deep1=Row1, deep2=101, deep3=false}
    ...
    MyEntity{field1='Row2', field2=101, field3=false, deep1=Row2, deep2=102, deep3=true}
    MyEntity{field1='Row2', field2=101, field3=false, deep1=Row2, deep2=102, deep3=false}
    

    进入SearchCriteria类的criteria字段是Comparator<MyEntity>类型的字段,或者是使用枚举或解析字符串表达式等的映射

  2. # 2 楼答案

    MyEntity不应实现Comparable。正是字段,您将根据这些字段对MyEntity对象列表进行排序,它们需要是Comparable。例如,如果按字段user排序,这是一个UserEntity,那么UserEntity是需要可比较的,而不是MyEntity

    lambda的工作应该只是检查字段是否确实是Comparable,如果不是,则抛出异常

    但是,由于您在编译时不知道字段的类型,因此必须在这里使用原始类型。comparing调用如下所示:

    Comparator.comparing(entity -> {
        try {
            Object fieldValue = sortByField.get(entity);
    
            // This check still passes if the type of fieldValue implements Comparable<U>, 
            // where U is an unrelated type from the type of fieldValue, but this is the
            // best we can do here, since we don't know the type of field at compile time
            if (!(fieldValue instanceof Comparable<?>) && fieldValue != null) {
                throw new IllegalArgumentException("Field is not comparable!");
            }
            return (Comparable)fieldValue;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    })