В этом руководстве мы рассмотрим, как отсортировать 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 для достижения этой
функции, как для стандартных, так и для настраиваемых компараторов в
порядке возрастания и убывания.