Java 8 帖子分组依据

标签 java grouping java-stream

我有一个 Job 类的对象列表,每个对象都有一个标签集合(网络),这个集合是可变的,对 hashCode 和对象相等性没有影响。

我需要做的是获取所有唯一 Job 对象的列表,并为每个此类对象组合所有标签,例如,我有一个列表:

[{职位:“CTO”,日期:“2012-2014”,城市:“纽约”,网络:[“foo”]},{职位:“CTO”,日期:“2012- 2014”,城市:“纽约”,网络:[“bar”]}]

这应该简化为[{position: "CTO",dates:"2012-2014",city:"New York",networks:["foo","bar"]}]

public class Job {
    private final String position;
    private final String dates;
    private final Integer startYear;
    private final Integer endYear;
    private final String city;
    private Set<NetworkType> networks;

    public String getPosition() {
        return position;
    }

    public String getDates() {
        return dates;
    }

    public String getCity() {
        return city;
    }

    public Set<NetworkType> getNetworks() {
        return networks;
    }

    public void setNetworks(Set<NetworkType> networks) {
        this.networks = networks;
    }

    public Integer getStartYear() {
        return startYear;
    }

    public Integer getEndYear() {
        return endYear;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Job)) {
            return false;
        }
        Job job = (Job) o;
        return Objects.equals(position, job.position) &&
                Objects.equals(dates, job.dates) &&
                Objects.equals(city, job.city);
    }

    @Override
    public int hashCode() {
        return Objects.hash(position, dates, city);
    }
}

这是实际的 Job 类代码,这就是我实现此操作的方式:

    Map<Job, List<Job>> jobsMap = jobs.stream().collect(Collectors.groupingBy(job -> job));
    jobsMap.keySet().stream()
            .peek(job -> jobsMap.get(job).stream().forEach(j -> job.getNetworks().addAll(j.getNetworks())))
            .sorted(Comparator.comparing((Job o) -> Objects.firstNonNull(o.getEndYear(), Integer.MAX_VALUE))
                    .reversed())
            .collect(Collectors.toList());

但是我对这段代码感觉非常糟糕,特别是因为我在流内使用外部映射,并且我想知道是否有任何方法可以在一个链中做到这一点而无需中间转换。 如果对我实现此功能有任何有效的批评,我将不胜感激。 谢谢!

最佳答案

假设我们将合并所有 networks进入特定的第一次出现Job我们发现,我们可以用一行(相当复杂的)来完成此操作:

import static java.util.stream.Collectors.*;
import static java.util.function.Function.identity;

Map<Job, Optional<Job>> collect = jobs.stream()
    .collect(groupingBy(identity(), reducing((l, r) -> {
        l.networks().addAll(r.networks());
        return l;
    })));

我使用了流畅的访问器,因为我懒得输入 get

所以。这是如何工作的?

首先我们stream jobs并调用Collectors.groupingByFunction.identity() ,这给了我们一个Map<Job, List<Job>> .

但我们不想要 List<Job> - 这就是Collectors.reducing进来。这作为下游 Collector 传递的groupingBy

下游Collector负责创造Map的值(value)部分- 在这种情况下,我们将所有找到的工作减少为一个 Job .

reducing((l, r) -> {
    l.networks().addAll(r.networks());
    return l;
}

所以这需要两个 Job项,并返回一项。这是一个折叠操作,所以 BiFunctionreturn值依次为每个 Job 。我们所做的就是添加所有 networks()从新Job到现有的Job .

显然这会给你一个Map<String, Optional<Job>> ,但折叠它是一项简单的工作。

我看不出有什么方法可以将其变成 List直接...


为了处理Map<Job, Optional<Job>>进入List<Job>可以使用以下内容:

collect.values().stream()
    .map(Optional::get)
    .collect(toList);

因此,您可以在一行中完成所有操作:

List<Job> collect = jobs.stream()
    .collect(groupingBy(identity(), reducing((l, r) -> {
        l.networks().addAll(r.networks());
        return l;
    })))
    .values().stream()
    .map(Optional::get)
    .collect(toList);

尽管如此,其可读性值得怀疑。

关于Java 8 帖子分组依据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35166306/

相关文章:

java - 使用java将日期值插入oracle不工作

java - 定时器程序卡住

java - 初始化后 OpenGL 窗口卡住

Java计算两个ArrayList或HashMap的队列

java - 在 Java 8 中,如何使用 lambda 将 Map<K,V> 转换为另一个 Map<K,V>?

java - Java 中的 Fail fast finally 子句

c# - 使用 Lambda 按名称中的常见文本对文件进行分组

r - 如何基于时间 block 创建唯一索引

c# - 如何按周对日期进行分组?

java - 使用嵌套流连接