Динамическая сортировка списка на основе выбранного пользователем свойства в java8 - PullRequest
1 голос
/ 01 мая 2020

У меня есть этот метод, который отлично работает, но есть ли более чистый способ сделать это?

В настоящее время я добавил компараторы на карту и получил правильный компаратор на основе выбранного пользователем значения.

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

Ответы [ 4 ]

1 голос
/ 01 мая 2020

Единственный ярлык, который я вижу, - это автоматизировать преобразование представления метода String в Function<? super BusinessPartnerAssignmentDetail, U> с использованием отражения.

Важное замечание: Это решение может быть полезно в huuuge количество добывающих и возможных комбинаций. Получатели должны быть формальными параметрами свободными (стандартные получатели). Я бы предпочел придерживаться вашего текущего решения, которое является более читабельным и обслуживаемым, которое я считаю приоритетным.

Решение и его описание:

  1. Извлечь Method из String, используя Class::getMethod.
  2. Создать Function<? super BusinessPartnerAssignmentDetail, U> из Method.
    • Использование Method::invoke
    • Тот же самый вызов метода вызывает 2 исключения, поэтому создание лямбда-выражения лучше поместить в отдельный метод (silentInv) для ясности.
  3. Верните правильный компаратор с естественным или обратным порядком на основе имени метода по сравнению со списком имен (я рекомендую сделать этот список stati c ).
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);
        }
    };
}

Подсказка: Используйте более короткие имена классов, если это возможно:)

0 голосов
/ 07 мая 2020

Вы можете обернуть все это в 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())).

Является ли это лучше, чем просто заполнение карты, ваш звонок.

0 голосов
/ 07 мая 2020

Попробуйте использовать ENUM для параметров сортировки и используйте кейс переключателя для получения конкретного компаратора:

DTO с параметрами фильтра Enum

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;
    }
}
0 голосов
/ 01 мая 2020

создание карты компараторов при каждом вызове метода - плохая идея.

вместо этого вы можете вернуть требуемый компаратор в 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;
    }

или вы можете создать карту внутри конструктора как глобальное поле, тогда вы не будете инициализировать карту каждый раз раз отозван.

...