Как группировать и собирать с помощью Java 8? - PullRequest
0 голосов
/ 04 мая 2018

У меня есть список элементов, назовем его «ключевые слова», например:

public class Keyword {
    Long id;
    String name;
    String owner;
    Date createdTime;
    Double price;
    Date metricDay;
    Long position;
}

Дело в том, что для каждого дня есть ключевое слово. Например:

Keyword{id=1, name="kw1", owner="Josh", createdTime="12/12/1992", price="0.1", metricDay="11/11/1999", position=109}
Keyword{id=1, name="kw1", owner="Josh", createdTime="12/12/1992", price="0.3", metricDay="12/11/1999", position=108}
Keyword{id=1, name="kw1", owner="Josh", createdTime="12/12/1992", price="0.2", metricDay="13/11/1999", position=99}
Keyword{id=2, name="kw2", owner="Josh", createdTime="13/12/1992", price="0.6", metricDay="13/11/1999", position=5}
Keyword{id=2, name="kw2", owner="Josh", createdTime="13/12/1992", price="0.1", metricDay="14/11/1999", position=4}
Keyword{id=3, name="kw3", owner="Josh", createdTime="13/12/1992", price="0.1", metricDay="13/11/1999", position=8}

Затем из этого списка я хотел бы создать новый список со всеми метриками всех этих разных дней в одном списке. Сначала я создал такой класс:

public class KeywordMetric {
    Double price;
    Date metricDay;
    Long position;
} 

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

public class KeywordMeged {
    Long id;
    String name;
    String owner;
    List<KeywordMetric> metricList;
}

Пример того, что я ожидаю:

KeywordMerged{id=1, name="kw1", owner="Josh", createdTime="12/12/1992", metricList=[KeywordMetric{price=0.1,metricDay="11/11/1999",position=109},KeywordMetric{price=0.3,metricDay="12/11/1999",position=108},KeywordMetric{price=0.2,metricDay="13/11/1999",position=99}]
KeywordMerged{id=2, name="kw2", owner="Josh", createdTime="13/12/1992", metricList=[KeywordMetric{price=0.6,metricDay="13/11/1999",position=5},KeywordMetric{price=0.1,metricDay="14/11/1999",position=4}]
KeywordMerged{id=3, name="kw3", owner="Josh", createdTime="13/12/1992", metricList=[KeywordMetric{price=0.1,metricDay="13/11/1999",position=8}]

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

Map<Long, List<Keyword>> kwL = kwList.stream()
                    .collect(groupingBy(Keyword::getId))

И я знаю, что с .forEach() я мог бы перебрать эту Карту, но не могу понять, как сделать метод collect() потоков, передаваемых из List в KeywordMerged.

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Немного другой подход. Сначала вы собираете карту ключевых слов, сгруппированных по идентификатору:

Map<Integer, List<Keyword>> groupedData = keywords.stream()
    .collect(Collectors.groupingBy(k -> k.getId()));

Далее вы конвертируете свою карту в список нужного формата:

List<KeywordMerged> finalData = groupedData.entrySet().stream()
    .map(k -> new KeywordMerged(k.getValue().get(0).getId(),
        k.getValue().stream()
            .map(v -> new KeywordMetric(v.getMetricDay(), v.getPrice(), getPosition()))
            .collect(Collectors.toList())))
    .collect(Collectors.toList());

Это будет работать с сгруппированными данными, но при преобразовании карты будет создан объект KeywordMerged, который в качестве аргумента получит идентификатор (вы можете расширить его самостоятельно) и преобразовать в List<KeywordMetric>, ранее сгруппированный по данным идентификатора.

РЕДАКТИРОВАТЬ: я считаю, что с некоторыми извлечения из методов вы можете сделать это выглядит намного лучше:)

0 голосов
/ 04 мая 2018

Вместо этого вы можете попробовать использовать Collectors.toMap(...). Где:

Keyword::getId - это функция сопоставления клавиш.

KeywordMerged.from(...) выполняет преобразование: Keyword => KeywordMerged

(left, right) -> { .. } объединяет метрики для сущностей с одинаковыми идентификаторами.

Collection<KeywordMerged> result = keywords.stream()
    .collect(Collectors.toMap(
        Keyword::getId,
        k -> KeywordMerged.from(k), // you can replace this lambda with a method reference
        (left, right) -> {
            left.getMetricList().addAll(right.getMetricList());
            return left;
        }))
    .values();

Метод преобразования может выглядеть примерно так:

public class KeywordMerged {
    public static KeywordMerged from(Keyword k) {            
        KeywordMetric metric = new KeywordMetric();
        metric.setPrice(k.getPrice());
        metric.setMetricDay(k.getMetricDay());
        metric.setPosition(k.getPosition());

        KeywordMerged merged = new KeywordMerged();
        merged.setId(k.getId());
        merged.setName(k.getName());
        merged.setOwner(k.getOwner());
        merged.setMetricList(new ArrayList<>(Arrays.asList(metric)));
        return merged;
    }
}

Я думаю, у вас есть основная идея. Итак, рефакторинг в соответствии с вашими потребностями ...

...