根据用户在 java8 中选择的 属性 对列表进行动态排序

Dynamically sort the list based on user selected property in java8

我有这个方法,效果很好,但是有没有更简洁的方法呢?

目前我已将比较器添加到地图中,并根据用户选择的值获得正确的比较器。

private Comparator<? super BusinessPartnerAssignmentDetail> getComparator(PortfolioFilterDto portfolioFilterDto){
    Map<String, Comparator<? super BusinessPartnerAssignmentDetail>> sortingOptions = new HashMap<>();
    sortingOptions.put("fieldOfficeDescription", Comparator.comparing(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.nullsLast(Comparator.naturalOrder())));
    sortingOptions.put("locationDescription", Comparator.comparing(BusinessPartnerAssignmentDetail::getLocationDescription, Comparator.nullsLast(Comparator.reverseOrder())));
    sortingOptions.put("segmentType", Comparator.comparing(BusinessPartnerAssignmentDetail::getSegmentType, Comparator.nullsLast(Comparator.reverseOrder())));
    sortingOptions.put("displayName", Comparator.comparing(BusinessPartnerAssignmentDetail::getDisplayName, Comparator.nullsLast(Comparator.reverseOrder())));

    return sortingOptions.get(portfolioFilterDto.getSortParameter());
}

然后我在列表中这样调用排序

businessPartnerAssignmentDetails.sort(getComparator(portfolioFilterDto));

每次调用该方法时都创建一个比较器映射是个坏主意。

相反,您可以 return switch case

中所需的比较器
    switch (portfolioFilterDto.getSortParameter()){
        case "fieldOfficeDescription" :
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.nullsLast(Comparator.naturalOrder()));
        case "locationDescription" :
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getLocationDescription, Comparator.nullsLast(Comparator.reverseOrder()));       
        case "segmentType" :
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getSegmentType, Comparator.nullsLast(Comparator.reverseOrder()));
        case "displayName" :
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getDisplayName, Comparator.nullsLast(Comparator.reverseOrder()));
        default :
            return null;
    }

或者您可以在构造函数中将地图创建为全局字段,这样您就不会在每次撤销地图时都初始化地图。

我看到的唯一捷径是使用反射自动将 String 方法表示转换为 Function<? super BusinessPartnerAssignmentDetail, U>

重要提示: 此解决方案可能对 huuuge 数量的吸气剂和可能的组合有用。吸气剂必须是无形式参数的(标准吸气剂)。我宁愿坚持您当前的解决方案,这种方式更具可读性和可维护性,我认为这是优先事项。

解决方法及其说明:

  1. 使用 Class::getMethodString 中提取 Method
  2. Method 创建 Function<? super BusinessPartnerAssignmentDetail, U>
    • 使用Method::invoke
    • 同一个方法调用抛出 2 个异常,因此为了清楚起见,lambda 表达式的创建最好将其包装到一个单独的方法 (silentInv) 中。
  3. Return 正确的比较器根据方法名称与名称列表(我建议将此列表设为静态)以自然或相反的顺序枯萎。
static List<String> naturalOrderList = Arrays.asList("fieldOfficeDescription");

static <U extends Comparable<U>> Comparator<? super BusinessPartnerAssignmentDetail> 
    getComparator(PortfolioFilterDto p) throws NoSuchMethodException 
{
    /** (1) **/ 
    Method method = BusinessPartnerAssignmentDetail.class.getMethod(p.getSortParameter());

    /** (2) **/ 
    Function<? super BusinessPartnerAssignmentDetail, U> function = silentInv(method);

    /** (3) **/ 
    Comparator<U> order = methodsWithNaturalOrders.contains(method.getName()) 
        ? Comparator.naturalOrder() 
        : Comparator.reverseOrder();
    return Comparator.comparing(function, Comparator.nullsLast(order));
}

@SuppressWarnings("unchecked")
static <U extends Comparable<U>> Function<? super BusinessPartnerAssignmentDetail, U> 
    silentInv(Method method) 
{
    /** (2) The necessary try-catch wrapping, the exception should never be thrown **/ 
    return bpad -> {
        try {
            return (U) method.invoke(bpad);
        } catch (Exception e) {
            String message = "Invalid method name " + method.getName();
            throw new IllegalArgumentException(message , e);
        }
    };
}

提示:尽可能使用较短的 class 名称:)

尝试使用 ENUM 来排序选项并使用 switch case 来获取特定的比较器:

带枚举过滤器选项的 DTO

public class PortfolioFilterDto {
/*your existing code
 *
 *
 * */
PortfolioFilterDtoOptions sortParameter;
enum PortfolioFilterDtoOptions {
    fieldOfficeDescription, locationDescription, segmentType, displayName
 }

public PortfolioFilterDtoOptions getSortParameter() {
    return this.sortParameter;
 }
}

您动态获取过滤器选项的方法

private Comparator<? super BusinessPartnerAssignmentDetail> getComparator(PortfolioFilterDto portfolioFilterDto) {
    switch (PortfolioFilterDto.getSortParameter()) {
        case PortfolioFilterDtoOptions.fieldOfficeDescription:
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.nullsLast(Comparator.naturalOrder()));
        case PortfolioFilterDtoOptions.locationDescription:
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getLocationDescription, Comparator.nullsLast(Comparator.reverseOrder()));
        case PortfolioFilterDtoOptions.segmentType:
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getSegmentType, Comparator.nullsLast(Comparator.reverseOrder()));
        case PortfolioFilterDtoOptions.displayName:
            return Comparator.comparing(BusinessPartnerAssignmentDetail::getDisplayName, Comparator.nullsLast(Comparator.reverseOrder()));
        default:
            // put default filter here
            return null;
    }
}

你可以像 Sourin 说的那样将所有这些都包装在一个 enum 中,但是你可以用更多的功能来丰富它,比如实际比较。

enum AssignmentFilter implements Comparator<BusinessPartnerAssignmentDetail> {
    fieldOfficeDescription(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.naturalOrder()),
    locationDescription(BusinessPartnerAssignmentDetail::getLocationDescription),
    segmentType(BusinessPartnerAssignmentDetail::getSegmentType),
    displayName(BusinessPartnerAssignmentDetail::getDisplayName);

    private Function<BusinessPartnerAssignmentDetail, Comparable<? super BusinessPartnerAssignmentDetail>> compareByKey;

    AssignmentFilter(Function<BusinessPartnerAssignmentDetail, Comparable<? super BusinessPartnerAssignmentDetail>> byKey, Comparator<BusinessPartnerAssignmentDetail> whenNull) {
        compareByKey = Comparator.comparing(byKey, Comparator.nullsLast(whenNull));
    }
    AssignmentFilter(Function<BusinessPartnerAssignmentDetail, Comparable<? super BusinessPartnerAssignmentDetail>> byKey) {
        this(byKey, Comparator.reverseOrder());
    }

    public int compare(BusinessPartnerAssignmentDetail bpad1, BusinessPartnerAssignmentDetail bpad1) {
        return comparator().compare(bpad1, bpad2);
    }
}

你可以通过businessPartnerAssignmentDetails.sort(AssingmentFilter.valueOf(portfolioFilterDto.getSortParameter()))调用它。

这是否比仅填充地图更好,由您决定。