2015-11-15 2 views
4

У меня есть коллекция, которая выглядит ниже, и я хочу отфильтровать все, кроме дат, которые не являются концом месяцев.Confused by Java8 Collectors.toMap

2010-01-01=2100.00, 
2010-01-31=2108.74, 
2010-02-01=2208.74, 
2010-02-28=2217.92, 
2010-03-01=2317.92, 
2010-03-31=2327.57, 
2010-04-01=2427.57, 
2010-04-30=2437.67, 
2010-05-01=2537.67, 
2010-05-31=2548.22, 
2010-06-01=2648.22, 
2010-06-30=2659.24, 
2010-07-01=2759.24, 
2010-07-31=2770.72, 
2010-08-01=2870.72, 
2010-08-31=2882.66, 
2010-09-01=2982.66, 
2010-09-30=2995.07, 
2010-10-01=3095.07, 
2010-10-31=3107.94, 
2010-11-01=3207.94, 
2010-11-30=3221.29 

У меня есть следующие критерии фильтрации. frequency.getEnd возвращает LocalDate, соответствующий концу месяца для данного LocalDate.

.filter(p -> frequency.getEnd(p.getKey()) == p.getKey()) 

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

.collect(Collectors.toMap(/* HUH? */)); 

Но я не знаю, что делать с Collectors.toMap. Примеры чтения оставляют меня в замешательстве. Вот мой текущий код, который, очевидно, не работает.

TreeMap<LocalDate, BigDecimal> values = values.entrySet() 
               .stream() 
               .filter(p -> frequency.getEnd(p.getKey()) == p.getKey()) 
               .collect(Collectors.toMap(/* HUH? */)); 

ответ

3

в дополнение к предыдущим ответам отметить, что если вам не нужно, чтобы сохранить оригинальную карту, вы можете выполнить такую ​​фильтрацию на месте без использования потока API:

values.keySet().removeIf(k -> !frequency.getEnd(k).equals(k)); 
2

Поскольку вы переборе Map.Entry ценности и toMap() просто нужно два метода для извлечения ключа и значения, это это просто:

Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue) 

Обратите внимание, что это будет не Вернуть TreeMap , Для этого вам необходимо:

Collectors.toMap(Entry::getKey, 
       Entry::getValue, 
       (v1,v2) -> { throw new IllegalStateException("Duplicate key"); }, 
       TreeMap::new) 
0

Метод toMap в своей простейшей форме принимает два аргумента: один функция для отображения входа на ключ, а другая функция для отображения входа на значение. Вывод обеих функций объединяется для формирования записи в полученной карте.

Я думаю, что вам нужно сделать что-то вроде этого:

Collectors.toMap(p -> p.getKey(), p -> p.getValue()) 
+0

OP хочет 'TreeMap' в качестве вывода, а не текущий по умолчанию' HashMap'. – Tunaki

+1

Я не вижу, что он специально хочет TreeMap. Он показывает пример, который использует TreeMap, что не делает его требованием. – Tom

15

Рассмотрим проблему так: у вас есть поток занесения карты, то есть сказать Stream<Map.Entry<LocalDate, BigDecimal>>, и вы хотите, чтобы забрать его в TreeMap<LocalDate, BigDecimal>.

Итак, вы правы, вы должны использовать Collectors.toMap. Теперь, как вы можете видеть в the documentation, есть на самом деле 3 Collectors.toMap, в зависимости от аргументов:

  • toMap(keyMapper, valueMapper). keyMapper - это функция, чей вход является текущим элементом потока и выход которого является ключом к окончательной карте. Таким образом, он сопоставляет элемент Stream с ключом (отсюда и название). valueMapper - это функция, чей вход является текущим элементом потока и выход которого является значением конечной карты.
  • toMap(keyMapper, valueMapper, mergeFunction). Первые два параметра такие же, как и раньше. Третий, mergeFunction, - это функция, которая вызывается в случае дублирования ключевых элементов в окончательной карте; поэтому его ввод - 2 значения (т. е. два значения, для которых keyMapper вернули один и тот же ключ) и объединяют эти два значения в один.
  • toMap(keyMapper, valueMapper, mergeFunction, mapSupplier). Первые три аргумента аналогичны предыдущим. Четвертый - поставщик Карты: поскольку в настоящее время он реализован в JDK, два предшествующих toMap возвращают экземпляр HashMap. Но если вам нужен конкретный экземпляр карты, этот поставщик вернет этот экземпляр.

В нашем конкретном случае, мы должны использовать третью toMap, потому что мы хотим, чтобы результат Карта явно быть TreeMap. Давайте посмотрим, какой вклад мы должны дать:

  • keyMapper: так что это должно вернуть ключ окончательной Карты. Здесь мы имеем дело с Stream<Map.Entry<LocalDate, BigDecimal>>, поэтому каждый элемент Stream имеет тип Map.Entry<LocalDate, BigDecimal>. Затем эта функция принимает входной сигнал Map.Entry<LocalDate, BigDecimal> e. Его выход должен быть ключом к окончательной карте, в этом случае выход должен быть e.getKey(), то есть LocalDate, который удерживает запись. Это можно записать в виде лямбда-выражения: e -> e.getKey(). Это также можно записать в виде ссылки на метод Map.Entry::getKey, но давайте придерживаться здесь лямбда, потому что это может быть проще понять.
  • valueMapper: это то же самое, что и выше, но в этом случае эта функция должна вернуть e.getValue(), то есть BigDecimal, что запись удерживается. Так что это e -> e.getValue().
  • mergeFunction: Это сложный вопрос. Мы знаем, что нет дублирующих ключевых элементов (т. Е. Дубликатов LocalDate) на конечной карте, по построению. Что мы пишем здесь? Простое решение состоит в том, чтобы выбросить исключение: этого не должно произойти, и если это произойдет, есть большая проблема. Итак, независимо от двух входных аргументов, мы вышлем исключение. Это можно записать как (v1, v2) -> { throw new SomeException(); }. Обратите внимание, что это необходимо заключить в скобки. В этом случае, и в соответствии с тем, что делает JDK в настоящее время, я выбрал SomeException как IllegalStateException.
  • mapSupplier: как уже было сказано, мы хотим поставить TreeMap. Поставщик не принимает аргументов и возвращает новый экземпляр. Так что здесь можно написать () -> new TreeMap<>(). Опять же, мы могли бы использовать ссылку на метод и написать TreeMap::new.

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

Collectors.toMap(
     e -> e.getKey(), // Map.Entry::getKey 
     e -> e.getValue(), // Map.Entry::getValue 
     (v1, v2) -> { throw new IllegalStateException(); }, 
     () -> new TreeMap<>()) // TreeMap::new 
) 
+0

Я бы, но слишком длинный – njzk2

+6

@ njzk2 Ну, я старался быть настолько явным, насколько мог. OP путается о коллекционерах вообще (и, может быть, лямбдах вообще), поэтому я думаю, что потратить время на объяснение - это хорошо. Я согласен, что в конце есть много разговоров для 6 строк кода, но я считаю, что это необходимо. – Tunaki

+0

Спасибо за внимание. Я думаю, что я потерял бы на mergeFunction (потому что я ничего не сливаю) и mapSupplier. Я закончил с некоторыми небольшими модами, которые я добавил в нижней части моего сообщения. – Patrick