Как заменить HashMap на LinkedhashMap с потоками?

Не могли бы вы помочь мне отсортировать мой Map?

У меня есть следующая структура, и я хочу собрать приложения для клиентов в LinkedHashMap в обратном порядке по значению.

[{
  "CUSTOMER_1": {
    "APP_1": "1"
  },
  "CUSTOMER_2": {
    "APP_1": "2",
    "APP_3": "7",
    "APP_2": "3"
  }
}]

У меня уже есть лямбда, которая собирает карту:

 final Map<String, Map<String, Long>> organizationApps = taskHandles.stream().map(this::mapPayload)
            .flatMap(e -> e.entrySet().stream())
            .collect(groupingBy(Map.Entry::getKey, of(HashMap::new,
                    (r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
                    (r1, r2) -> {
                        r2.forEach((v1, v2) -> r1.merge(v1, v2, Long::sum));
                        return r1;
                    }))
            );

Теперь мне нужно заменить значения organizationApps, представленные HashMap, на LinkedHashMap и поместить значения в LinkedHashMap в правильном порядке.

Как я могу сделать это в лямбде?

УПД:

Я могу добиться необходимого результата с помощью следующего фрагмента кода:

    final Map<String, Map<String, Long>> organizationApps = taskHandles.stream().map(this::mapPayload)
            .flatMap(e -> e.entrySet().stream())
            .collect(groupingBy(Map.Entry::getKey, of(HashMap::new,
                    (r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
                    (r1, r2) -> {
                        r2.forEach((v1, v2) -> r1.merge(v1, v2, Long::sum));
                        return r1;
                    }))
            );

    organizationApps.forEach((key, value) -> {
        final Map<String, Long> sorted = value.entrySet()
                .stream()
                .sorted(reverseOrder(Map.Entry.comparingByValue()))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                        LinkedHashMap::new));
        organizationApps.put(key, sorted);
    });

Как я могу сделать это в одной лямбде?


person Pasha    schedule 29.10.2018    source источник
comment
Вот связанный с этим вопрос: stackoverflow.com/questions/29721095/. Важной частью является то, что вам нужно правильно отсортировать поток перед сбором.   -  person Ryan Cogswell    schedule 29.10.2018
comment
Привет Райан! Я думаю, что мне нужно отсортировать его после того, как я соберу карту, потому что изначально у меня может быть 2 списка с картами с дублирующимися ключами, и я объединяю карты, суммируя значения   -  person Pasha    schedule 29.10.2018
comment
У меня следующий вопрос: зачем вам это нужно делать в одной лямбде? Даже если это возможно - у вас, вероятно, будет действительно большой и уродливый кусок кода. И мне уже трудно понять, что происходит в вашем коде. ИМХО лямбды не всегда лучшее решение для такого рода проблем. Как указано здесь - лямбды должны быть выражением, а не Повествование. Несмотря на лаконичный синтаксис, лямбда-выражения должны точно выражать предоставляемые ими функции.   -  person rxn1d    schedule 29.10.2018
comment
1) Потому что я хочу посмотреть на результат и сравнить его со следующим 2) Я хотел бы получить больше знаний о лямбда-выражениях   -  person Pasha    schedule 29.10.2018
comment
Могут быть разные способы организации операций, но, в конце концов, вам нужен результат слияния/суммы для приложений, прежде чем вы сможете выполнить сортировку, поэтому я думаю, что это всегда будет включать (по крайней мере) два логических шага.   -  person Ryan Cogswell    schedule 29.10.2018
comment
Основной вопрос, чтобы развеять некоторые сомнения, укажите, что такое taskHandles и какие статические импорты вы использовали. Сделало бы вопрос более ясным.   -  person Naman    schedule 29.10.2018
comment
taskHandles — это List‹TaskHandle›, Map‹String, Map‹String, Long›› mapPayload — это функция, которая извлекает полезную нагрузку из каждой задачи, и у меня есть статический импорт методов java.util.stream.Collector... of(), groupingBy(), toMap() и java.util.Collections.reverseOrder;   -  person Pasha    schedule 29.10.2018
comment
Райан, так что это мой вопрос - где и как я должен вставить шаг сортировки   -  person Pasha    schedule 29.10.2018
comment
Вы уже сделали это в своем обновлении двумя отдельными шагами (хотя на самом деле их гораздо больше, чем два, поскольку каждый вызов связанного метода в потоке — это отдельный шаг).   -  person Ryan Cogswell    schedule 29.10.2018
comment
Райан, и я хотел бы знать, как сделать это в одном потоке   -  person Pasha    schedule 29.10.2018
comment
Да, я это понимаю, но смысл моего предыдущего комментария в том, что я не думаю, что это возможно. Вам нужно два прохода через ваши данные, так как вам нужен полный результат первого потока, прежде чем можно будет выполнить работу по сортировке второго потока.   -  person Ryan Cogswell    schedule 29.10.2018
comment
Может как-то в функции слияния в коллекторе?   -  person Pasha    schedule 29.10.2018
comment
Правильный порядок - обратный порядок. Я не создаю LinkedHashMap, потому что на этом шаге я объединяю значения по дубликатам ключей из разных карт. Поэтому в первую очередь я должен суммировать значения по дублирующимся ключам и только после этого сортировать   -  person Pasha    schedule 30.10.2018
comment
Хорошо, проблема в том, что вы задали вопрос по Java, но предоставили только какой-то JSON для описания вашего ввода. Насколько можно распознать, вы вызываете stream() некоторые taskHandles неизвестного типа, сопоставляете элементы, вызывая this.mapPayload(…), чтобы преобразовать эти элементы неизвестного типа во что-то другое неизвестного типа, после чего следует сложная операция collect, которую мы должны понять без какой-либо подсказки о фактическом вводе. Возможно, решение есть, но только не тогда, когда нам нужно сначала провести обратный инжиниринг вашего кода.   -  person Holger    schedule 30.10.2018
comment
После того, как я сделаю mapPayload, просто верните Map‹String, Map‹String, Long››. Итак, после этого шага у меня есть список карт   -  person Pasha    schedule 30.10.2018
comment
Вы имеете в виду поток карт?   -  person Holger    schedule 30.10.2018
comment
Да, точно. JSON представляет этот промежуточный шаг   -  person Pasha    schedule 30.10.2018


Ответы (1)


Вы не можете избежать сбора значений на карту, прежде чем вы сможете создать LinkedHashMap, упорядоченный по результирующим значениям, но вы можете указать шаг преобразования как функцию finisher вашего пользовательского сборщика:

Map<String, Map<String, Long>> organizationApps = input.stream()
    .flatMap(e -> e.entrySet().stream())
    .collect(groupingBy(Map.Entry::getKey, Collector.of(HashMap::new,
        (r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
        (r1, r2) -> {
            r2.forEach((v1, v2) -> r1.merge(v1, v2, Long::sum));
            return r1;
        },
        (Map<String, Long> m) -> m.entrySet().stream()
            .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
            .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                LinkedHashMap::new))
    )));

который будет эквивалентен следующему коду (Java 9+)

Map<String, Map<String, Long>> organizationApps = input.stream()
    .flatMap(m -> m.entrySet().stream())
    .collect(groupingBy(Map.Entry::getKey, collectingAndThen(flatMapping(
            t -> t.getValue().entrySet().stream(),
            toMap(Map.Entry::getKey, Map.Entry::getValue, Long::sum)),
        (Map<String, Long> m) -> m.entrySet().stream()
            .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
            .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                LinkedHashMap::new))
    )));

В конце этот ответ представляет собой реализацию сборщика flatMapping, совместимую с Java 8, но ваш собственный сборщик также выполняет эту работу. .

person Holger    schedule 30.10.2018
comment
Спасибо, это именно то, что я искал! - person Pasha; 30.10.2018