简体   繁体   English

如何使用Java 8进行分组和收集?

[英]How to groupBy and Collect with Java 8?

I have a list of elements, let's call it "keywords", like this: 我有一个元素列表,将其称为“关键字”,如下所示:

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

The thing is that there is a keyword for every single day. 问题是每一天都有一个关键字。 For example: 例如:

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}

Then, from this list I would like to create a new list with all the metrics from all those different days on one single list. 然后,从该列表中,我想创建一个新列表,将来自所有不同日期的所有指标合并到一个列表中。 First, I created a class like this: 首先,我创建了一个像这样的类:

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

And what I would like to archive is go from the first list, to a structure like this: 我要存档的是从第一个列表转到这样的结构:

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

Example of what I expect: 我期望的示例:

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}]

I know how to do this with a lot of loops and mutable varibles, but I can't figure out how to do this with streams and lambda operations. 我知道如何使用许多循环和可变变量来执行此操作,但是我不知道如何使用流和lambda操作来执行此操作。 I was able to group all related keywords by Id with this: 我可以使用ID将所有相关关键字分组:

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

And I know that with .forEach() I could iterate over that Map, but can't figure out how to make the collect() method of streams pass from List to KeywordMerged. 而且我知道使用.forEach()可以遍历该Map,但无法弄清楚如何使流的collect()方法从List传递到KeywordMerged。

You can try to use the Collectors.toMap(...) instead. 您可以尝试使用Collectors.toMap(...) Where: 哪里:

Keyword::getId is a key mapper function. Keyword::getId是一个键映射器函数。

KeywordMerged.from(...) performs a transformation: Keyword => KeywordMerged KeywordMerged.from(...)执行转换: Keyword => KeywordMerged

(left, right) -> { .. } combines metrics for entities with identical ids. (left, right) -> { .. }组合具有相同ID的实体的指标。

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();

A transformation method might look something like this: 转换方法可能看起来像这样:

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;
    }
}

I think you've got the basic idea. 我认为您已经有了基本的想法。 So, refactor according to your needs... 因此,请根据您的需求进行重构...

A slightly different approach. 略有不同的方法。 First you collect the Map of keywords grouped by id: 首先,您收集按ID分组的关键字地图:

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

Further you convert your map to the list of desired format: 进一步,您将地图转换为所需格式的列表:

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());

This will work on the grouped data, but transforming the map it will create KeywordMerged object, which as argument will receive id (you can extent it further yourself) and converted to List<KeywordMetric> previously grouped by ID data. 这将对分组数据起作用,但是转换地图将创建KeywordMerged对象,该对象将作为参数接收ID(您可以自己进一步扩展),并转换为先前按ID数据分组的List<KeywordMetric>

EDIT: I believe with some extraction to methods you can make it look much nicer :) 编辑:我相信与一些方法的提取,您可以使其看起来更好:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM