В этом руководстве мы рассмотрим, как отсортировать 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
качестве значений. В
большинстве случаев вы встретите Integer
или String
качестве ключей
и настраиваемые объекты String
или Integer
качестве значений. Нам
нужно отсортировать эту HashMap
на основе ключей String
HashMap
в любом случае не гарантирует сохранения порядка элементов.
Порядок может меняться со временем, и они определенно не будут
напечатаны обратно в порядке вставки:
John=21
Mark=31
Maria=34
Sydney=24
Если вы повторно запустите эту программу, она сохранит этот порядок,
поскольку HashMap
упорядочивает свои элементы по ячейкам на основе
хэш-значения ключей. При печати значений из HashMap
его содержимое
печатается последовательно, поэтому результаты останутся такими же, если
мы повторно запустим программу несколько раз.
Сортировка HashMap по ключу с помощью TreeMap
TreeMap
расширяет SortedMap
, в отличие от реализации HashMap
TreeMap
s предназначены для отсортированного аналога, однако,
TreeMap
S только сортировать по ключам, учитывая компаратор.
Сортировка строковых ключей лексикографически
Создать TreeMap
учетом HashMap
так же просто, как предоставить вызов
конструктора с несортированной картой:
Map<String, Integer> sortedMap = new TreeMap<>(unsortedMap);
sortedMap.entrySet().forEach(System.out::println);
Выполнение этого кода приводит к:
John=21
Maria=34
Mark=31
Sydney=24
Поскольку мы не предоставили никакого компаратора, срабатывает
компаратор по умолчанию, используемый для Strings. В частности, когда вы
сравниваете Strings, метод compareTo()
сравнивает лексикографическое
значение каждой String и сортирует их в порядке возрастания.
Мы увидим имена, начинающиеся с A
, перед именами, начинающимися с B
и т. Д. Давайте добавим два новых имени и посмотрим, что произойдет:
unsortedMap.put("Adam", 35);
unsortedMap.put("Aaron", 22);
Map<String, Integer> sortedMap = new TreeMap<>(unsortedMap);
sortedMap.entrySet().forEach(System.out::println);
Это приводит к:
Aaron=22
Adam=35
John=21
Maria=34
Mark=31
Sydney=24
Сортировка ключей с помощью настраиваемого компаратора
Действительно приятной особенностью является то, что мы можем
предоставить new Comparator<T>()
для TreeMap
и указать в нем нашу
собственную логику сравнения. Например, давайте посмотрим, как мы можем
отсортировать строковые ключи по длине в HashMap
, используя
length
строк и настраиваемый компаратор:
Map<String, Integer> sortedMap = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int lengthDifference = o1.length() - o2.length();
if (lengthDifference != 0) return lengthDifference;
return o1.compareTo(o2);
}
});
sortedMap.putAll(unsortedMap);
sortedMap.entrySet().forEach(System.out::println);
Здесь мы TreeMap
с настраиваемым Comparator
а в переопределенном
compare()
мы указали желаемую логику.
Поскольку у нас нет гарантии, что o1.length() - o2.length()
не будет
равно 0, простой оператор if гарантирует, что мы сравниваем их
лексикографически, если их длина одинакова.
Затем, когда мы определили критерии сортировки для TreeMap
, мы
использовали putAll()
метод , чтобы вставить все элементы из
unsortedMap
минуты sortedMap
.
Выполнение этого кода приводит к:
Adam=35
John=21
Mark=31
Aaron=22
Maria=34
Sydney=24
Карта теперь сортируется с помощью специального Comparator
, который в
этом случае сравнивает length
s ключей String
Здесь вы можете
использовать любую логику для удовлетворения ваших конкретных
потребностей.
Сортировка HashMap по ключу с LinkedHashMap
LinkedHashMap
сохраняет порядок вставки. Он хранит двусвязный список
всех записей, что позволяет вам очень естественно обращаться к его
элементам и перебирать их.
Итак, самый простой способ преобразовать несортированный HashMap
в
LinkedHashMap
- это добавить элементы в том порядке, в котором мы
хотели бы, чтобы они располагались.
Сортировка ключей HashMap лексикографически
Теперь давайте unsortedMap
, создав новый LinkedHashMap
который
будет содержать элементы в отсортированном порядке.
У Map.Entry
есть очень удобный метод, который здесь вступает в игру -
comparingByKey()
, который сравнивает ключи, если у них есть
допустимые методы сравнения. Поскольку мы имеем дело со String
s, это
метод compareTo()
, который снова отсортирует String
s
лексикографически:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.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
созданный с помощью comparingByKey()
, который сравнивает данные объекты с их реализацией сравнения по
умолчанию.
После сортировки мы collect()
с помощью Collectors.toMap()
в новую
карту. Конечно, мы будем использовать те же ключи и значения из исходной
карты через Map.Entry::getKey
и Map.Entry::getValue
.
Наконец, создается новый LinkedHashMap
, в который вставляются все эти
элементы в отсортированном порядке.
Выполнение этого кода приводит к:
Aaron=22
Adam=35
John=21
Maria=34
Mark=31
Sydney=24
Сортировка ключей HashMap с помощью настраиваемого компаратора
В качестве альтернативы вы можете использовать свой собственный
Comparator
вместо того, который сгенерирован
Map.Entry.comparingByKey()
. Это так же просто, как предоставить
Comparator.comparing()
и передать ему действительное
лямбда-выражение :
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparing(e -> e.getKey().length()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
Здесь мы воссоздали наш настраиваемый компаратор, который сортирует
ключи по их значению из предыдущих разделов. Теперь String
будут
отсортированы по их длине, а не по их лексикографическому значению:
Adam=35
John=21
Mark=31
Aaron=22
Maria=34
Sydney=24
Конечно, вы можете легко переключиться с восходящего на нисходящий
порядок, просто добавив -
перед e.getKey().length()
:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparing(e -> -e.getKey().length()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
Это приводит к:
Sydney=24
Aaron=22
Maria=34
Adam=35
John=21
Mark=31
Кроме того, вы можете использовать другие компараторы, такие как
Comparator.comparingInt()
если вы имеете дело с целочисленными
значениями (хотя мы здесь, общий компаратор также работает),
Comparator.comparingDouble()
или Comparator.comparingLong()
чтобы
удовлетворить твои нужды.
Заключение
В этом руководстве мы рассмотрели, как отсортировать Java HashMap по
ключу . Первоначально мы использовали TreeMap
для сортировки и
поддержания порядка отсортированных записей, используя как компаратор по
умолчанию, так и настраиваемый компаратор.
Затем у нас есть потоки Java 8 с LinkedHashMap
для достижения этой
функции, как для стандартных, так и для настраиваемых компараторов в
порядке возрастания и убывания.