Сортировка List<> по значению поля
Всем привет!
У меня вопрос касательно того, как можно красиво и наиболее правильно оформить сортировку List<> по значению.
Какого-то определённого термина обозначения данной сортировки я не нашёл, поэтому назвал её "по значению" (если кто-то считает, что можно навзвать более выраженно, то милости просим).
Итак, в чём суть:
Есть класс Product
@Data
public class Product {
/**
* Тип продукта
*/
private ProductType productType;
/**
* Доступность продукта (продукт может быть временно заблокирован и не доступен)
*/
private Boolean isAvailable;
private enum ProductType {
NEW,
OLD,
REMOVED,
UNUSED,
EXCLUSIVE
}
}
Предположим, что данный класс хранится в Базе Данных и когда я его вытаскиваю от туда, то получаю List<Product>, а затем я должен вернуть его на FRONTEND в отсортированном виде.
Заказчик выдвинул требования.
Упорядоченность в списке должна быть следующая:
- ProductType == EXCLUSIVE И isAvailable=true
- ProductType == NEW И isAvailable=true
- ProductType == OLD И isAvailable=true
- ProductType == UNUSED И isAvailable=true
- ProductType == REMOVED И isAvailable=true
- Все оставшиеся продукты у которых isAvailable=false
Вопрос насущный: как правильно организовать такого рода сортировку.
Если вы хотите знать смысл такого рода сортировки, то это не должно быть вашей заботой. ВАМ выдали ВОТ ТАКИЕ ТРЕБОВАНИЯ и нужно сделать так, как требуют.
У меня была идея сделать что-то вроде такого, а именно модернизировать класс Product, добавив метод для определения приоритета, а потом использовать Comparator.
@Data
public class Product {
/**
* Тип продукта
*/
private ProductType productType;
/**
* Доступность продукта (продукт может быть временно заблокирован и не доступен)
*/
private Boolean isAvailable;
public int getPriority() {
if (productType.equals(ProductType.EXCLUSIVE) && BooleanUtils.isTrue(isAvailable)) {
return 1;
} else if (productType.equals(ProductType.NEW) && BooleanUtils.isTrue(isAvailable)) {
return 2;
} else if (productType.equals(ProductType.OLD) && BooleanUtils.isTrue(isAvailable)) {
return 3;
} else if (productType.equals(ProductType.UNUSED) && BooleanUtils.isTrue(isAvailable)) {
return 4;
} else if (productType.equals(ProductType.REMOVED) && BooleanUtils.isTrue(isAvailable)) {
return 5;
}
return 6;
}
private enum ProductType {
NEW,
OLD,
REMOVED,
UNUSED,
EXCLUSIVE
}
}
Использование
private static Comparator<Product> PRODUCT_SORT = Comparator.comparing(Product::getPriority)
.reversed();
Но возможно есть более лаконичные способы, поэтому прошу всех желающих предложить свой вариант.
Ответы (2 шт):
Можно сделать массив с нужным порядком элементов и при сортировке сравнивать по индексу
var values = List.of("98", "99", "86", "41", "56", "46");
var list = new ArrayList<>(List.of("99", "99", "86", "46", "98", "98", "56", "41", "46"));
list.sort(Comparator.comparingInt(values::indexOf));
[98, 98, 99, 99, 86, 41, 56, 46, 46]
Если вы хотите знать смысл такого рода сортировки, то это не должно быть вашей заботой. ВАМ выдали ВОТ ТАКИЕ ТРЕБОВАНИЯ и нужно сделать так, как требуют.
Дело в том, что как раз важно понимать, почему требования такие. В этом случае, в частности, важно такой порядок важен только в этом конкретном сценарии, или он обусловлен порядком "важности" типов продуктов в любом сценарии. Я тут использую слово "важность", но правильней будет использовать то слово, которое используют в данной предметной области.
Реализация будет правильней и лучше, если модель предметной области будет ей соответствовать. А данном случае на практике это означает, что если тип продукта имеет "важность" (или "приоритет") сам по себе независимо от сценария, то имеет смысл это явно выразить в модели, это можно сделать добавив поле в enum:
public int getPriority() {
return BooleanUtils.isTrue(isAvailable) ? productType.getOrder() : 6;
}
private enum ProductType {
NEW(1),
OLD(2),
REMOVED(3),
UNUSED(4),
EXCLUSIVE(5);
int order; // это и есть "важность"
ProductType(int order) {
this.order = order;
}
int getOrder() {
return this.order;
}
}
Если же порядок сортировки зависит от сценария, то тогда, скорее неправильным будет размещать getPriority в Product. Лучше задать порядок для сценария например так:
ImmutableMap<ProductType, Integer> MAIN_PAGE_PRODUCT_ORDER = ImmutableMap.of(
NEW, 1,
OLD, 2,
REMOVED, 3,
UNUSED, 4,
EXCLUSIVE, 5
);
private static Comparator<Product> sortBy(ImmutableMap<ProductType, Integer> productTypeOrder) {
return Comparator.comparing(
p -> p.isAvailable() ? productTypeOrder.get(p.getProductType()) : 6)
.reversed();
}