Collectors.groupingBy (Function, Supplier, Collector) не принимает лямбда-выражения / не видит потоковые значения - PullRequest
0 голосов
/ 04 февраля 2019

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

Мои данные:

List<String> stringList = new ArrayList<>();
stringList.add("Key:1,2,3")
stringList.add("Key:5,6,7")

Ключ - это ключ на карте, а 1,2,3 - это значения на карте

Сначала я попытался использовать простой toMap

Map<String, List<Integer>> outputKeyMap = stringList.stream()
 .collect(Collectors.toMap(id -> id.split(":")[0], 
          id-> Arrays.stream(id.split(":")[1].split(",")).collect(Collectors.toList());

, но он не работает, потому что он всегда создает один и тот же ключ.Поэтому мне нужно использовать функцию groupingBy.

Map<String, List<Integer>> outputKeyMap = stringList.stream().collect(groupingBy(id -> id.toString().split(":")[0],
            TreeMap::new,
            Collectors.mapping(id-> Arrays.stream(id.toString().split(":")[1].split(","))
                            .map(Integer::valueOf)
                            .collect(Collectors.toSet()))));

Но в этом решении компилятор не видит значений, переданных в лямбда-функции, и я не понимаю, почему Function является первым параметром, итакже в Collectors.mapping.В этом решении поток не работает.

Collectors.groupingBy (Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream)

Редактировать: почему не работает функция groupingBy

Я забыл добавить Collectors.toSet () в Collectors.mapping в качестве второго параметра.Но затем я получаю Сет в Сете, так что это не то, что я ищу.Должно быть использовано flatMapping, но оно есть в Java9.

    Map<String, Set<Set<String>>> collect = stringList.stream()
                    .collect(groupingBy(id -> id.split(":")[0],
                    TreeMap::new,
                    Collectors.mapping(id-> Arrays.stream(id.toString().split(":")[1].split(","), 
                    Collectors.toSet())

Ответы [ 2 ]

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

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

Map<String, List<Integer>> result = stringList.stream()
        .map(string -> string.split(":"))
        .collect(Collectors.toMap(
                 splitted -> splitted[0],
                 splitted -> Arrays.stream(splitted[1].split(","))
                                   .map(Integer::valueOf)
                                   .collect(Collectors.toCollection(ArrayList::new)),
                 (l1, l2) -> { l1.addAll(l2); return l1; }));

Здесь (l1, l2) -> { l1.addAll(l2); return l1; } - это функция слияния.Он будет вызываться коллектором при каждом столкновении клавиш.Поскольку List.addAll изменяет список, мы должны убедиться, что первый созданный список является изменяемым, следовательно, использование .collect(Collectors.toCollection(ArrayList::new)) в функции отображения значений.

Я также оптимизировал первое разбиениев Stream.map операцию, которая вызывается перед сбором, что позволяет избежать разбиения более одного раза.


Приведенное выше решение не удаляет дубликаты из списков.Если вам это нужно, вместо этого вы должны набрать Set:

Map<String, Set<Integer>> result = stringList.stream()
        .map(string -> string.split(":"))
        .collect(Collectors.toMap(
                 splitted -> splitted[0],
                 splitted -> Arrays.stream(splitted[1].split(","))
                                   .map(Integer::valueOf)
                                   .collect(Collectors.toCollection(LinkedHashSet::new)),
                 (s1, s2) -> { s1.addAll(s2); return s1; }));

Обратите внимание, что LinkedHashSet сохраняет порядок вставки.

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

Если у вас нет дубликатов ключей в вашем списке источников, вы можете получить Map<String, List<Integer>>, например:

 Map<String, List<Integer>> result = stringList.stream()
            .collect(toMap(string -> string.split(":")[0],
                    string -> Arrays.stream(string.split(":")[1].split(","))
                            .map(Integer::valueOf)
                            .collect(toList())));

Если у вас есть дубликаты ключей, есть способ с flatMapping из java9:

Map<String, List<Integer>> result = stringList.stream()
           .collect(groupingBy(s -> s.split(":")[0], 
                        flatMapping(s -> Arrays.stream(s.split(":")[1].split(","))
                                           .map(Integer::valueOf), 
                        toList())));

Выходные данные будут содержать все целочисленные значения для Key:

{Key=[1, 2, 3, 5, 6, 7]}

...