Сортировка данных через Интерфейс Comparator
Нужна помощь в сортировке через Интерфейс Comparator или Stream.
Задача , нужно отсортировать значения Map по убыванию.Найти наименьшее значение и вернуть его ключ, сохранив в переменную, если элементов с наименьшим значением несколько взять первый. Люди в интернете советуют использовать Comparator или Stream.sorted(), но я новичок в java , и не могу понять как это реализовать .
Любая помощь приветствуется, если можно на примере .
Есть такой код, с тестовыми данными:
List<Map<String,Object>> managers = new ArrayList<>();
Map<String,Object> user1 = new HashMap<>(),user2 = new HashMap<>(),user3 = new HashMap<>(),user4 = new HashMap<>();
user1.put("id","777");
user1.put("name","user1");
user2.put("id","888");
user2.put("name","user2");
user3.put("id","999");
user3.put("name","user3");
user4.put("id","1000");
user4.put("name","user4");
//Достаю из листа , ид всех пользователей.
List listId = managers.stream().map(m->m.get("id")).collect(Collectors.toList());
// в задаче, уже есть данные которые надо отсортировать, лежат они в Map answer
// имеют вид {777=0104, 888=0102, 999=0103, 1000=0102}
Map<String,String> answer = new HashMap<>();
//Пробовал так не получилось: зато вывожу в лог , объекты которые хочу сравнить.
//Думаю перед сравнением , строку надо преобразовать в int , Integer.parseInt(answer.get(a))
listId.sort((a,b)-> {
System.out.println("answer.get(a) " + answer.get(a));
System.out.println("answer.get(b) " + answer.get(b));
return answer.get(a).compareTo(answer.get(b));//Здесь наверное нужен Comparator
});
//Дальше сам компаратор
Collections.sort(listId,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int r;
Integer n1 = Integer.parseInt(answer.get(o1));
Integer n2 = Integer.parseInt(answer.get(o2));
//Здесь сравнить значения?
return r;
}
});
//или как можно написать через
listId.stream().sorted();
Ответы (3 шт):
public class Main {
public static void main(String[] args) {
Map<String, String> answer = new HashMap<>();
answer.put("777", "0104");
answer.put("888", "0102");
answer.put("999", "0103");
answer.put("1000", "0102");
Map.Entry<String,String> res = answer.entrySet().stream().min(Map.Entry.comparingByValue((o1, o2) -> {
int n1 = Integer.parseInt(o1);
int n2 = Integer.parseInt(o2);
return n1 - n2;
})).orElse(null);
System.out.println(res);
}
}
888=0102
Если задача сводится к поиску минимального элемента в списке по какому-либо параметру, то сортировка этого списка может быть излишней, что в целом приведёт к повышению вычислительной сложности, так как поиск минимума имеет линейную сложность O(N), а быстрая сортировка в среднем O(N log N).
Также следует отметить, что вместо использования Map<String, Object> для хранения данных о пользователях правильнее было бы создать некий простой класс (POJO) или же кортеж (record) в Java 16 и выше. В частности, так будет проще найти полную информацию о первом пользователе, для id которого зафиксировано минимальное значение в мапе answers.
Для этого следует воспользоваться поиском минимума в стриме Stream::min, в который следовало бы передать компаратор. Поскольку Stream::min возвращает Optional<T>, в данном случае для непустого входного списка можно применить Optional.get
record User(String id, String name) {}
List<User> managers = Arrays.asList(
new User("777", "user1"), new User( "888", "user2"),
new User("999", "user3"), new User("1000", "user4")
);
Map<String, String> answers = Map.of(
"777", "0104", "888", "0102", "999", "0103", "1000", "0102"
);
User firstMin = managers.stream()
.min(Comparator.comparingInt(
u -> Integer.parseInt(answers.get(u.id()))
)) // Optional<User>
.get();
System.out.println(firstMin); // -> User[id=888, name=user2]
Если существует вероятность, что в списке managers окажется элемент, для которого не будет соответствия в мапе answers, для обращения к элементам мапы можно использовать Map::getOrDefault, возвращающее слишком большое значение, например, строковое представление Integer.MAX_VALUE:
List<User> managers2 = Arrays.asList(
new User("777", "user1"), new User( "888", "user2"),
new User("999", "user3"), new User("2000", "user5")
);
User firstMin2 = managers2.stream()
.min(Comparator.comparingInt(
u -> Integer.parseInt(answers.getOrDefault(
u.id(), String.valueOf(Integer.MAX_VALUE)
))
))
.get();
В случае пустого входного списка минимальный элемент не будет обнаружен, и тогдаOptional::get выбросит исключение java.util.NoSuchElementException: No value present.
мой вариант решения задачи , благодаря подсказкам:
List<Map<String,Object>> managers2 = Arrays.asList(
ImmutableMap.of(
"id","777",
"name","user1"
),
ImmutableMap.of(
"id","888",
"name","user2"
),
ImmutableMap.of(
"id","999",
"name","user3"
),
ImmutableMap.of(
"id","1000",
"name","user4"
)
);
Map<String, String> answers = ImmutableMap.of(
"777", "0104", "888", "0102", "999", "0103", "1000", "0102"
);
List<Map<String,Object>> sortedTest = managers2.stream().sorted(
(first, second) -> {
int n1 = Integer.parseInt(answers.getOrDefault(first.get("id"),"9999"));
int n2 = Integer.parseInt(answers.getOrDefault(second.get("id"),"9999"));
return Integer.compare(n1, n2);
}
).collect(Collectors.toList());
System.out.println("Отсортированные данные: " + sortedTest);
System.out.println("Первый элемент с наименьшим значением: " + sortedTest.remove(0));
System.out.println("Остальные элементы: " + sortedTest);