В этом руководстве мы рассмотрим, как отсортировать HashMap по значению в Java .
Давайте продолжим и создадим простую HashMap
:
Map<String, Integer> unsortedMap = new HashMap();
unsortedMap.put("John", 21);
unsortedMap.put("Maria", 34);
unsortedMap.put("Mark", 31);
unsortedMap.put("Sydney", 24);
unsortedMap.entrySet().forEach(System.out::println);
У нас есть String
качестве ключей и Integer
качестве значений. И мы
хотели бы отсортировать эту карту по значениям.
HashMap
в любом случае не гарантирует сохранения порядка элементов.
Порядок может меняться со временем, и они определенно не будут
напечатаны обратно в порядке вставки:
John=21
Mark=31
Maria=34
Sydney=24
Если вы повторно запустите эту программу, она сохранит этот порядок,
поскольку HashMap
упорядочивает свои элементы по ячейкам на основе
хэш-значения ключей. При печати значений из HashMap
его содержимое
печатается последовательно, поэтому результаты останутся такими же, если
мы повторно запустим программу несколько раз.
Примечание. TreeMap
расширяет SortedMap
, в отличие от
реализации HashMap
TreeMap
s предназначены для отсортированного
аналога, однако, TreeMap
S только сортировать по ключам, учитывая
компаратор.
Сортировка HashMap по значению с помощью LinkedHashMap
LinkedHashMap
сохраняет порядок вставки. Он хранит двусвязный список
всех записей, что позволяет вам очень естественно обращаться к его
элементам и перебирать их.
Итак, самый простой способ преобразовать несортированный HashMap
в
LinkedHashMap
- это добавить элементы в том порядке, в котором мы
хотели бы, чтобы они располагались.
Сортировка HashMap в порядке возрастания
Чтобы отсортировать unsortedMap
мы видели ранее, мы создадим новую
LinkedHashMap
чтобы разместить элементы в том порядке, в котором мы
хотим, чтобы они располагались.
Начнем с сортировки HashMap
в порядке возрастания:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparingInt(e -> e.getValue()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
То , что мы сделали здесь потоковый unsortedMap
набора «s из
Map.Entry
объектов. Затем, используя метод sorted()
, мы можем
использовать различные Comparator
чтобы указать, как сравниваются
записи.
Поскольку мы имеем дело с простыми целыми числами, мы можем легко
использовать Comparator.comparingInt()
и передать
лямбда-выражение . С помощью этого
выражения мы передаем ключ сортировки из типа T
(в нашем случае
Integer
). Затем этот метод возвращает Comparator
который сравнивает
этот ключ сортировки.
После того, как они сортируются, мы можем collect()
их в новую карту,
через Collectors.toMap()
вызова, где мы используем тот же
Map.Entry::getKey
и Map.Entry::getValue
, как в unsortedMap
.
Наконец, создается новый LinkedHashMap
, в который вставляются все эти
элементы в отсортированном порядке.
Выполнение этого кода приводит к:
John=21
Sydney=24
Mark=31
Maria=34
В качестве альтернативы вместо Comparator.comparingInt()
вы можете
использовать Map.Entry.comparingByValue()
:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
Однако при таком подходе вы не можете указать собственную логику для сравнений. Сопоставимые значения, такие как целые числа, сортируются с помощью внутренней реализации. Однако вы можете указать настраиваемый объект и указать свою собственную логику сравнения в этом классе.
Аналогично этому, вы также можете просто использовать
Map.Entry::getValue
:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparingInt(Map.Entry::getValue))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
Это также возвращает:
John=21
Sydney=24
Mark=31
Maria=34
Этот функционально точно такой же, как и предыдущий, поскольку
Map.Entry.comparingByValue()
в любом случае использует метод
getValue()
для сравнения записей.
Сортировка HashMap в порядке убывания
Теперь давайте отсортируем несортированный HashMap
в порядке
убывания. Единственное отличие, которое вам нужно сделать, - это
лямбда-выражение, которое мы предоставили Comparator.comparingInt()
-
вместо этого мы просто используем -e.getValue()
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparingInt(e -> -e.getValue()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
Это приводит к:
Maria=34
Mark=31
Sydney=24
John=21
Это дополнительное преимущество использования этого подхода вместо
Map.Entry.comparingByValue()
или Map.Entry::getValue
. Вы можете
легко переключаться между порядком убывания и возрастания.
Заключение
В этом руководстве мы рассмотрели, как отсортировать Java HashMap по
значению . Мы использовали Java 8 Streams с LinkedHashMap
для
достижения этой функциональности, как для сортировки по возрастанию, так
и по убыванию значений.