Сортировка коллекции по N произвольным полям
Имеется коллекция элементов, в которых может быть N полей. К примеру, 5 полей:
class Human {
private String name;
private int age;
private int weight;
private int height;
private int condition;
...
}
В зависимости от выбора пользователя, нужно отсортировать коллекцию от 0 до 5 полей в произвольном порядке, как по возрастанию, так и по убыванию. То есть пользователь может захотеть отсортировать только по полям name и weight, или по полям height, weight, name и т.п., причем важен как порядок полей так и порядок сортировки.
Возможно ли как-то гибко реализовать это?
Или только пошагово - анализируем количество полей, анализируем последовательность полей, анализируем направление сортировки для каждого поля, и в зависимости от всех этих условий уже выбираем заранее написанный Comparator?
Ответы (1 шт):
В классе Human имплементируем интерфейс Comparable и указываем в нем карту полей для сравнения - LinkedHashMap с порядком их значимости и направлениями сортировки. Определяем метод compareTo, в котором проводим разбор карты:
public class Human implements Comparable<LinkedHashMap<String, Object>> {
private String name;
private Integer age;
public Human(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
@Override
public int compareTo(LinkedHashMap<String, Object> conditions) {
if (conditions == null) {
return 0;
}
int val = 0;
for (Map.Entry<String, Object> entry : conditions.entrySet()) {
Object value = entry.getValue();
switch (entry.getKey()) {
case ("name:asc"): {
if (value instanceof String) {
val = this.name.compareTo((String) value);
if (val != 0) {
return val;
}
}
break;
}
case ("name:desc"): {
if (value instanceof String) {
val = this.name.compareTo((String) value);
if (val != 0) {
return val * -1;
}
}
break;
}
case ("age:asc"): {
if (value instanceof Integer) {
val = this.age.compareTo((Integer) value);
if (val != 0) {
return val;
}
}
break;
}
case ("age:desc"): {
if (value instanceof Integer) {
val = this.age.compareTo((Integer) value);
if (val != 0) {
return val * -1;
}
}
break;
}
}
}
return val;
}
@Override
public String toString() {
return "Human{name=" + name + ",age=" + age + "}";
}
}
Подготавливаем карту полей и направлений сортировки и передаем в метод compareTo:
public static void main(String[] args) throws Exception {
ArrayList<Human> humans = new ArrayList<>();
humans.add(new Human("Sidr", 22));
humans.add(new Human("John", 23));
humans.add(new Human("Petr", 24));
humans.add(new Human("Sidr", 20));
humans.stream()
.sorted((h1, h2) -> {
LinkedHashMap<String, Object> conditions = new LinkedHashMap<>();
conditions.put("name:desc", h2.getName());
conditions.put("age:asc", h2.getAge());
return h1.compareTo(conditions);
})
.forEach(System.out::println);
}
Вывод:
Human{name=Sidr,age=20}
Human{name=Sidr,age=22}
Human{name=Petr,age=24}
Human{name=John,age=23}
Примечание: instanceof - дорогая по времени операция. Вместо нее можно использовать конструкцию try catch, т. к. при приведении типов могут выбрасываться исключения.