Вложенная группировка коллекторов с несколькими полями

Как бы вы использовали коллекторы для группировки по нескольким полям на 2-м уровне. Например:

"someList": {
        "firstLevelElementX": {
              "secondLevelElementW": 2,
              "secondLevelElementZ": 3,
              "secondLevelElementK": 7
        },
        "firstLevelElementY": {
              "secondLevelElementW": 1,
              "secondLevelElementZ": 3,
              "secondLevelElementK": 10
        }
}

Я попытался создать класс с элементами "secondLevel" и сгруппировать по этому классу, но не смог заставить его работать:

@Data
@AllArgsConstructor
public class someClass{
    private String firstLevelElement;
    private Long secondLevelElementW;
    private Long secondLevelElementZ;
    private Long secondLevelElementK= 0L;

}

И вот как я это делаю:

Map<String,Map<String,Long>> someList =
            events.stream().collect(
                Collectors.groupingBy(
                    someDAO::getFirstLevelElement,
                    Collectors.groupingBy(
                        someClass::getSecondLevelFields,
                        Collectors.counting())
                )
            );

Есть некоторые решения, которые предлагают группировку на n-уровне (с использованием цепочки), но я предпочитаю сделать ее менее сложной и более чистой, если это возможно.

Редактировать

Я попытаюсь привести лучший пример, чтобы прояснить себя, это список, который у меня есть:

{
      "date": "2019-04-08 08:28:01.0",
      "source": "maint",
      "severity": "HARMLESS",
      "site": "USA",
      "hostname": "usaHost"
    },
    {
      "date": "2019-04-08 08:28:01.0",
      "source": "CPU_Checker",
      "severity": "MINOR",
      "site": "GERMANY",
      "hostname": "germanyHost"
    },
    {
      "date": "2019-04-02 08:28:01.0",
      "source": "maint",
      "severity": "HARMLESS",
      "site": "USA",
      "hostname": "anotherUsaHost"
    }

Я хочу использовать группировку по «источнику» и «серьезности» на втором уровне, поэтому вывод должен выглядеть так:

"eventList": {
        "USA": {
              "maint": 2,
              "HARMLESS": 2
        },
        "GERMANY": {
              "CPU_checker": 1,
              "MINOR": 1
        }
}

Вы пытаетесь суммировать значения из secondLevelElementW, secondLevelElementZ и secondLevelElementK? Что такое метод someClass::getSecondLevelFields? Непонятно, что вы пытаетесь сделать, когда говорите «группировать по нескольким полям на 2-м уровне».

Karol Dowbecki 10.04.2019 13:29

Да, у меня есть List<someDAO>, и каждый DAO содержит несколько полей (поле1, поле2, поле3). Я хочу, чтобы поле1 было элементом на первом уровне, а под ним на втором уровне я хочу суммировать значения поля2 и поля3. (Пожалуйста, см. пример выше для 'someList')

user3819295 10.04.2019 17:23
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
2
1 849
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете суммировать значения полей второго уровня во втором коллекторе groupingBy:

Map<String, Map<Long, List<someClass>>> result = elements.stream().collect(
        groupingBy(someClass::getFirstLevelElement,
            groupingBy(s -> 
                s.getSecondLevelElementK() + s.getSecondLevelElementW() + s.getSecondLevelElementZ()))
);

Интересно, спасибо, посмотрю. Как я могу просмотреть эту карту, чтобы распечатать результаты?

user3819295 10.04.2019 18:10

Вы можете просто System.out.println(result) посмотреть, что внутри, если вам не нужен конкретный формат.

Karol Dowbecki 10.04.2019 18:13

Спасибо за ваш ответ, но, к сожалению, я не могу его принять, так как он не отвечает на вопрос. Я сделал редактирование, чтобы лучше объяснить себя.

user3819295 10.04.2019 19:02
Ответ принят как подходящий

Если вы можете использовать Ява 9 или выше, вы можете использовать Collectors.flatMapping() для достижения этого:

Map<String, Map<String, Long>> eventList = list.stream()
        .collect(Collectors.groupingBy(MyObject::getSite, Collectors.flatMapping(
                o -> Stream.of(o.getSource(), o.getSeverity()),
                Collectors.groupingBy(Function.identity(), Collectors.counting())
        )));

Результат будет таким:

{
    USA = {maint=2, HARMLESS=2}, 
    GERMANY = {CPU_Checker=1, MINOR=1}
}

Если вы не можете использовать Ява 9, вы можете реализовать функцию flatMapping() самостоятельно. Вы можете взглянуть на Collectors.flatMapping Java 9, переписанный в Java 8, который должен помочь вам в этом.

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