Свести результат группы потоков по нескольким ключам - PullRequest
0 голосов
/ 14 февраля 2019

Я хочу использовать потоки в Java для группировки длинного списка объектов на основе нескольких полей.Это приведет к карте map of map of map of .... of list list.

Как я могу извлечь списки только из этого сложного потока?

Вот примеркод для демонстрации (список строк, поиск групп с одинаковой длиной и первой буквой).Я не заинтересован в ключах, просто в результирующих сгруппированных объектах.

List<String> strings = ImmutableList.of("A", "AA", "AAA", "B", "BB", "BBB", "C", "CC", "CCC", "ABA", "BAB", "CAC");

Map<Character, Map<Integer, List<String>>> collect = strings.stream().collect(
        groupingBy(s -> s.charAt(0),
                groupingBy(String::length)
        )
);

Это даст следующий результат

My Map =
{
    A =
    {
        1 = [A]
        2 = [AA]
        3 = [AAA, ABA]
    }
    B =
    {
        1 = [B]
        2 = [BB]
        3 = [BBB, BAB]
    }
    C =
    {
        1 = [C]
        2 = [CC]
        3 = [CCC, CAC]
    }
}

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

 [
     [A],
     [AA],
     [AAA, ABA],
     [B],
     [BB],
     [BBB, BAB],
     [C],
     [CC],
     [CCC, CAC]
 ]

Ответы [ 3 ]

0 голосов
/ 14 февраля 2019

Вместо создания вложенных групп с использованием каскадного Collectors.groupingBy, вы должны сгруппировать по составному ключу:

Map<List<Object>, List<String>> map = strings.stream()
        .collect(Collectors.groupingBy(s -> Arrays.asList(s.charAt(0), s.length())));

Затем просто захватите значения карты:

List<List<String>> result = new ArrayList<>(map.values());

Есливы на Java 9+, вы можете изменить значение с Arrays.asList на List.of для создания составных ключей.

Этот подход очень хорошо подходит для вашего случая, потому что вы заявили, что не заинтересованы в сохранении ключей, и что реализация List, возвращаемая как Arrays.asList, так и List.of, четко определена в терминахих equals и hashCode методов, то есть их можно безопасно использовать в качестве ключей в любом Map.

0 голосов
/ 14 февраля 2019

Я хочу использовать потоки в java для группировки длинного списка объектов на основе нескольких полей.

Это хитрее, чем ваш (недействительный) пример кода, который заставляет меня думать, что вы ожидаете.Тем не менее, вы можете сгладить поток с помощью соответствующего метода flatMap().Для потока, который вы описываете, вам может потребоваться несколько раз сгладить или определить собственный метод отображения или сложную лямбду, чтобы сгладить все до того, что вы хотите.

В случаеКарта формы представлена ​​в вопросе, вы можете сделать что-то вроде этого:

List<List<String>> result = myMap.values().stream()
        .flatMap(m -> m.values().stream())  // as many of these as needed
        .collect(Collectors.toList());
0 голосов
/ 14 февраля 2019

Если вы хотите получить List<List<String>>, как в вашем примере, вы можете использовать:

List<List<String>> list = collect.entrySet().stream()
            .flatMap(e -> e.getValue().entrySet().stream())
            .map(Map.Entry::getValue)
            .collect(Collectors.toList());

Также, если вы хотите получить один список строк, вы можете добавить еще одну flatMap операцию:

     ...
     .flatMap(e -> e.getValue().entrySet().stream())
     .flatMap(e -> e.getValue().stream())
     ...

Как упомянул @Джон Боллинджер, использование потока значений, но не записей, будет проще.

...