【问题标题】:Dynamically sort the list based on user selected property in java8基于java8中用户选择的属性动态排序列表
【发布时间】:2020-08-16 06:02:14
【问题描述】:

我有这个方法很好用,但是有什么更简洁的方法吗?

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

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));

【问题讨论】:

    标签: java lambda java-8 comparator


    【解决方案1】:

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

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

    解决方案及其说明:

    1. 使用Class::getMethodString中提取Method
    2. Method 创建Function&lt;? super BusinessPartnerAssignmentDetail, U&gt;
      • 使用Method::invoke
      • 同样的方法调用会引发 2 个异常,因此为了清楚起见,最好将 lambda 表达式创建封装到一个单独的方法 (silentInv) 中。
    3. 根据方法名称与名称列表的比较,以自然或反向顺序返回正确的比较器(我建议将此列表设为静态)。
    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);
            }
        };
    }
    

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

    【讨论】:

      【解决方案2】:

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

      相反,您可以在 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;
          }
      

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

      【讨论】:

        【解决方案3】:

        尝试使用 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;
            }
        }
        

        【讨论】:

        • 为什么要加开关?向枚举添加一个方法以返回比较器。
        • 其实这样更好
        【解决方案4】:

        您可以像 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()))

        这是否比仅仅填写地图更好是您的决定。

        【讨论】:

          猜你喜欢
          • 2018-06-23
          • 1970-01-01
          • 1970-01-01
          • 2013-07-01
          • 2014-05-21
          • 2020-02-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多