Преобразовать карту <String, Object> в карту <String, Set <Object>> с фильтром и потоками - PullRequest
0 голосов
/ 02 января 2019

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

{
  key="someKey1", value=Apple(id="1", color="green"),
  key="someKey2", value=Apple(id="2", color="red"),
  key="someKey3", value=Apple(id="3", color="green"),
  key="someKey4", value=Apple(id="4", color="red"),
}

, в другую карту, на которой все яблоки одного цвета помещаются в один и тот же список:

{
  key="red", value=list={apple1, apple3},
  key="green", value=list={apple2, apple4},  
}

Iпробовал следующее:

Map<String, Set<Apple>> sortedApples = appleMap.entrySet()
    .stream()
    .collect(Collectors.toMap(l -> l.getColour, ???));

Я на правильном пути?Должен ли я использовать фильтры для этой задачи?Есть ли более простой способ?

Ответы [ 4 ]

0 голосов
/ 02 января 2019

Вы спросили, как сделать это с потоками, но вот еще один способ:

Map<String, Set<Apple>> result = new LinkedHashMap<>();
appleMap.values().forEach(apple -> 
    result.computeIfAbsent(apple.getColor(), k -> new LinkedHashSet<>()).add(apple));

Используется Map.computeIfAbsent, который либо возвращает набор, сопоставленный этому цвету, либо помещает пустой LinkedHashSet в карту, если еще ничего не сопоставлено с этим цветом, затем добавляет яблоко в набор .

РЕДАКТИРОВАНИЕ: Я использую LinkedHashMap и LinkedHashSet для сохранения порядка вставки, но мог бы использовать HashMap и HashSet соответственно.

0 голосов
/ 02 января 2019

Вы можете использовать Collectors.groupingBy и Collectors.toSet()

Map<String, Set<Apple>> sortedApples = appleMap.values() // Collection<Apple>
        .stream() // Stream<Apple>
        .collect(Collectors.groupingBy(Apple::getColour, // groupBy colour
                Collectors.mapping(a -> a, Collectors.toSet()))); // collect to Set
0 голосов
/ 02 января 2019

Если вы хотите продолжить с toMap, вы можете получить результат следующим образом:

map.values()  // get the apples
   .stream() // Stream<Apple>
   .collect(toMap(Apple::getColour, // group by colour
             v ->  new HashSet<>(singleton(v)), // have values as set of apples
          (l, r) -> {l.addAll(r); return l;})); // merge colliding apples by colour
  • поток по карте values вместо entrySet, потому что нас не интересуют ключи карты.
  • Apple::getColour - это функция keyMapper, используемая для извлечения "вещи", которую мы хотим сгруппировать, в данном случае цвет Apple s.
  • v -> new HashSet<>(singleton(v)) - это функция valueMapper, используемая для результирующих значений карты
  • (l, r) -> {l.addAll(r); return l;} - это функция слияния, используемая для объединения двух HashSet при столкновении клавиш на цвете Apple.
  • наконец, в результате получается карта Map<String, Set<Apple>>

но это лучше с groupingBy и toSet в качестве нисходящего потока:

map.values().stream().collect(groupingBy(Apple::getColour, toSet()));
  • поток по карте values вместо entrySet, потому что нас не интересуют ключи карты.

  • группирует Apple по предоставленной функции классификации, то есть Apple::getColour, а затем собирает значения в наборе, следовательно, toSet нисходящий коллектор.

  • наконец, в результате получается карта Map<String, Set<Apple>>

короткий, читаемый и идиоматический подход.

Вы также можете сделать это без потока:

Map<String, Set<Apple>> res = new HashMap<>();
map.values().forEach(a -> res.computeIfAbsent(a.getColour(), e -> new HashSet<>()).add(a));
  • итерация по карте values вместо entrySet, потому что нас не интересуют ключи карты.
  • , если указанный ключ a.getColour() еще не связан со значением, пытается вычислить его значение, используя заданную функцию отображения e -> new HashSet<>(), и вводит его в карту. затем мы добавляем Apple к результирующему набору.
  • , если указанный ключ a.getColour() уже связан со значением computeIfAbsent, возвращает существующее значение, связанное с ним, и затем мы вызываем add(a) для HashSet, чтобы ввести Apple в набор.
  • наконец, итоговая карта представляет собой Map<String, Set<Apple>>
0 голосов
/ 02 января 2019

Collectors.groupingBy больше подходит, чем Collectors.toMap для этой задачи (хотя могут использоваться оба).

Map<String, List<Apple>> sortedApples = 
    appleMap.values()
            .stream()
            .collect(Collectors.groupingBy(Apple::getColour));

Или, чтобы сгруппировать их в Set s, используйте:

Map<String, Set<Apple>> sortedApples = 
    appleMap.values()
            .stream()
            .collect(Collectors.groupingBy(Apple::getColour,
                                           Collectors.mapping(Function.identity(),
                                                              Collectors.toSet())));

или (как прокомментировал Аомин):

Map<String, Set<Apple>> sortedApples = 
    appleMap.values()
            .stream()
            .collect(Collectors.groupingBy(Apple::getColour, Collectors.toSet()));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...