2016-07-20 2 views
4

У меня есть карта с ключом как строка и значение как список. Список может иметь 10 уникальных значений. Мне нужно преобразовать эту карту с ключом как Integer и значение как List. Пример, как показано ниже:Потоки Java8: транспонировать карту со значениями как список

Входной сигнал:

"Ключ-1": 1,2,3,4

"Ключ-2": 2,3,4,5

"Ключ-3": 3,4,5,1

Ожидаемый результат:

1: "Ключ-1", "Key-3"

2: "Ключ-1", "Ключ-2"

3: "Ключ-1", "Ключ-2", "Ключ-3"

4: "Ключ-1" "Ключ-2", "Key-3"

5: "Ключ-2", "Key-3"

Я знаю, что использование для петель я могу добиться этого, но я должен был знать не может это делается через streams/lamda в java8.

-Спасибо.

ответ

2

Это немного страшно (я вообще стараюсь разбить его, чтобы сделать его более удобным для чтения), но вы можете сделать это следующим образом:

Map<Integer, List<String>> transposeMap = new HashMap<>(); 

map.forEach((key, list) -> list.stream().forEach(
    elm -> transposeMap.put(elm, 
     transposeMap.get(elm) == null ? Arrays.asList(key) : (Stream.concat(transposeMap.get(elm).stream(), 
      Arrays.asList(key).stream()).collect(Collectors.toList()))))); 

Предполагая Map<String, List<Integer>> map ваша оригинальная карта, что вы хотите перенести. transposeMap перенесет карту, которая вам нужна.

+1

Streams которые лучше подходят для операций без гражданства.Хотя этот ответ верен, он лучше подходит к императивному стилю. Это в основном стратегия цикла OP, но вместо этого заменяется потоком 'forEach'. – 4castle

+1

'forEach' не принадлежит' stream'. Он объявлен в 'Iterable',' list.stream(). ForEach' должен быть 'list.forEach'. –

+1

@ Jean-François Savard: У потоков также есть метод 'forEach', хотя и с немного другой семантикой. – Holger

11

Идея может быть, чтобы генерировать все значение ключа пару из исходной карты, а затем групповых ключей от этих значений:

import java.util.AbstractMap.SimpleEntry; 

import static java.util.stream.Collectors.groupingBy; 
import static java.util.stream.Collectors.mapping; 
import static java.util.stream.Collectors.toList; 

... 

Map<Integer, List<String>> transposeMap = 
    map.entrySet() 
     .stream() 
     .flatMap(e -> e.getValue().stream().map(i -> new SimpleEntry<>(i, e.getKey()))) 
     .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList()))); 
4

Alexis’ answer содержит общее решение для такого рода задач, используя flatMap и А временный держатель для комбинации ключевого и сплющенного значения. Единственная альтернатива избежать создания временных объектов держателя, чтобы повторно реализовать логику groupingBy коллектора и вставив цикл по списку значений логики в агрегатной функцию:

Map<Integer, List<String>> mapT = map.entrySet().stream().collect(
    HashMap::new, 
    (m,e) -> e.getValue().forEach(
       i -> m.computeIfAbsent(i,x -> new ArrayList<>()).add(e.getKey())), 
    (m1,m2) -> m2.forEach((k,v) -> m1.merge(k, v, (l1,l2)->{l1.addAll(l2); return l1;}))); 
1

Вы можете достичь в таким образом

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

Map<SEX,List<Person>> 

Я бы просто написать

Map<SEX,List<Person>> map = personList.stream() 

          .collect(Collectors.groupingBy(Person::getGender)); 

он получит мне что-то, как показано ниже (один ключ от нескольких значений)

key:MALE 
age31sexMALE 
age28sexMALE 

key:FEMALE 
age40sexFEMALE 
age44sexFEMALE 
Смежные вопросы