Сортировка карты с использованием потоков Java

Как сначала отсортировать карту с помощью ключа, а также отсортировать значения.

Map<String, List<String>> myMap = new HashMap<>();
myMap.put("Apple", Arrays.asList("iphone", "imac"));
myMap.put("Samsung", Arrays.asList("galaxys", "galaxyz"));
myMap.put("LG", Arrays.asList("ultra", "tab"));

//sort logic
Sort with the key first {Apple, LG, Samsung} and then sort the values based on the key.

Результат:

imac, iphone, tab, ultra, galaxys, galaxyz

поэтому в основном вы хотите, чтобы каждый список сортировался независимо (сначала), а затем сортировался по ключу и все списки (значения) были объединены.

user85421 19.06.2024 15:57

Правильный. Сначала отсортируйте ключ и на основе отсортированного ключа отсортируйте список значений (объединенный).

DarkCrow 19.06.2024 16:01

Если вы хотите, чтобы карта была отсортирована по ключу, почему бы не использовать TreeMap?

David Conrad 19.06.2024 16:01

Во-первых, поток по записям карты. Следовательно, отсортируйте их с помощью компаратора Map.Entry.comparingByKey(). Теперь, когда у вас есть список записей, отсортированный по ключам, сопоставьте значения (списки), отсортировав их с помощью Collections.sort(). Следовательно, плоская карта и сбор в список.

Matteo NNZ 19.06.2024 16:08

У вас есть серьезная проблема: HashMap неупорядочен, поэтому его сортировка не имеет смысла. Есть LinkedHashMap для порядка вставки или TreeMap extends SortedMap для... отсортированной карты.

Rogue 19.06.2024 16:45
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
5
134
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4


public static  Map<String, List<String>> sortMapList(Map<String, List<String>> myMap) {
        Map<String, List<String>> sortedMap = new TreeMap<>();
        myMap.entrySet().stream()
            .forEach(x -> sortedMap.put(x.getKey(), x.getValue().stream()
            .sorted().collect(Collectors.toList()))
            );
        return sortedMap;
    }

Это возвращает карту, а не плоский список. Кроме того, тот факт, что вы инициализируете sortedMap как HashMap, не дает вам никаких гарантий, что ключи все равно будут отсортированы при выполнении цикла. также есть способ создать список напрямую через сбор без необходимости использования промежуточного объекта.

Matteo NNZ 19.06.2024 16:28

Map.Entry.comparingByKey() сравнивает Map.Entry в естественном порядке по ключу.

Anilal 19.06.2024 17:35

Вы перебираете карту по порядку, но помещаете ее обратно в неупорядоченный HashMap, в результате чего сортируются только List. Сортировка по ключу фактически игнорируется.

ipodtouch0218 19.06.2024 17:54

Вместо myMap.entrySet().stream().forEach(x -> …), вызывая x.getKey() и x.getValue() в функции, вы можете в первую очередь использовать myMap.forEach((key, value) -> …).

Holger 20.06.2024 10:19
Ответ принят как подходящий

Проще решить проблемы потока, составив план. Примером этой проблемы может быть:

  1. Получите поток с карты, используя пары Ключ, Значение (Stream<Map.Entry<String, List<String>>>)
  2. Сортировать по ключам
  3. Получить значение (списки) из отсортированных ключей; чтобы получить поток списков в правильном порядке (Stream<List<String>>)
  4. Сортировка списков
  5. Объедините элементы каждого списка в один поток (Stream<String>)
  6. Соберите поток в итоговый список (List<String>)

Сначала вам придется преобразовать карту в entrySet, чтобы обеспечить потоковую передачу по записям Map.Entry. Затем мы можем отсортировать списки с помощью ключа карты с помощью .sorted(Map.Entry.comparingByKey()) и .map(Map.Entry::getValue).

Когда списки отсортированы, элементы внутри не сортируются. Поэтому нам нужно сгладить их (перейти от Stream<List<String>> к Stream<String>, содержащему элементы списков) в отсортированном порядке: .flatMap(list -> list.stream().sorted())

Результат: код:

List<String> result = myMap.entrySet().stream() // Step 1
    .sorted(Map.Entry.comparingByKey())         // Step 2
    .map(Map.Entry::getValue)                   // Step 3
    .flatMap(list -> list.stream().sorted())    // Step 4+5
    .collect(Collectors.toList());              // Step 6

Вы также можете пропустить сопоставление с Map.Entry::getValue, выполнив .flatMap(entry -> entry.getValue().stream().sorted()) напрямую, но тогда будет немного менее понятно, что происходит.

Или 4+5 можно использовать ссылки на методы .map(List::stream).flatMap(Stream::sorted)

DuncG 19.06.2024 17:53

Или пойдите в другом направлении и объедините шаги с 3 по 5 в один, .flatMap(e -> e.getValue().stream().sorted())

Holger 20.06.2024 10:11

Обратите внимание, что HashMap неупорядочен: вообще не существует понятия сортировки HashMap. При изменении карты порядок клавиш может измениться непредсказуемо. В этом случае мы можем использовать TreeMap (который расширяет SortedMap), чтобы избежать проблемы с порядком HashMap:

Map<String, List<String>> myMap = new TreeMap<>();
myMap.put("Apple", Arrays.asList("iphone", "imac"));
myMap.put("Samsung", Arrays.asList("galaxys", "galaxyz"));
myMap.put("LG", Arrays.asList("ultra", "tab"));

Это позволяет вам получать отсортированный вывод напрямую с потоками (обратите внимание на вызов #sort внутри #flatMap):

List<String> sorted = myMap.values().stream().flatMap(l -> l.sort().stream()).toList();
// ["imac", "iphone", "tab", "ultra", "galaxys", "galaxyz"]

Лучшим подходом может быть сохранение внутренних списков (читай: наборов), отсортированных для начала:

Map<String, Set<String>> myMap = new TreeMap<>();
myMap.put("Apple", new TreeSet<>(Arrays.asList("iphone", "imac")));
myMap.put("Samsung", new TreeSet<>(Arrays.asList("galaxys", "galaxyz")));
myMap.put("LG", new TreeSet<>(Arrays.asList("ultra", "tab")));

Теперь ваши значения всегда будут отсортированы, даже при обновлении:

myMap.get("Apple").add("ipad");
List<String> sorted = myMap.values().stream().flatMap(Set::stream).toList();
// ["imac", "ipad", "iphone", "tab", "ultra", "galaxys", "galaxyz"]

Обратите внимание, что это решение предпочтительнее, если вы можете контролировать тип карты, но не в том случае, если вам просто дали Map<X,Y> и вам нужно отсортировать ее таким образом. Хотя в вопросе не упоминается, так это или нет.

ipodtouch0218 19.06.2024 16:58

Если это так, они могут взглянуть на ваш ответ :). Обратите внимание, что в Java 16 введен Stream#toList для удобства набора текста.

Rogue 19.06.2024 17:05

@ ipodtouch0218 прав, Карта предоставлена, и я не могу ее контролировать. Таким образом, данная карта, к сожалению, является HashMap.

DarkCrow 19.06.2024 17:18

Вот своего рода гибридное решение.

  • создай ArrayList<String>
  • отсортируйте значения Entry на месте.
  • затем отсортируйте карту по ключам и итеративно добавьте предварительно отсортированные списки в список результатов.
 List<String> result = new ArrayList<>();
 myMap.entrySet().forEach(e -> e.getValue().sort(null));
 myMap.entrySet().stream()
         .sorted(Entry.comparingByKey())
         .map(Entry::getValue)
         .forEach(lst -> result.addAll(lst));

принты

[imac, iphone, tab, ultra, galaxys, galaxyz]  

Вместо myMap.entrySet().forEach(e -> e.getValue().sort(null)); вы можете использовать myMap.values().forEach(v -> v.sort(null));, myMap.values().forEach(Collections::sort); или myMap.forEach((k, v) -> v.sort(null));. Но имейте в виду, что это предполагает, что списки в исходной карте изменяемы.

Holger 20.06.2024 10:14

@Holger Просто не рассматривал возможность прямого использования значений. Но я учитывал изменчивость списков. Основываясь на данных и конструкции, предоставленных ОП, я решил, что все в порядке. Спасибо за предложения!

WJS 20.06.2024 13:21

Да, в примере кода вопроса указана изменчивость. Но это может быть слишком упрощенный пример, поэтому стоит явно упомянуть об ограничениях.

Holger 21.06.2024 11:48

Другие вопросы по теме