Java8POST分组方法

Java8POST分组方法,java,grouping,java-stream,Java,Grouping,Java Stream,我有一个Job类的对象列表,每个对象都有一个标记集合(网络),这个集合是可变的,对hashCode和对象的平等性没有影响 我需要做的是获得所有唯一作业对象的列表,并为每个此类对象组合所有标记,例如,我有一个列表: public class Job { private final String position; private final String dates; private final Integer startYear; private final I

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

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

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);
    }
}
这应该减少到
[{职位:“CTO”,日期:“2012-2014”,城市:“纽约”,网络:[“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);
    }
}
公共类作业{
私有最终字符串位置;
私人最终字符串日期;
私有最终整数startYear;
私人最终整数年底;
私人城市;
专用集网络;
公共字符串getPosition(){
返回位置;
}
公共字符串getDates(){
返回日期;
}
公共字符串getCity(){
回归城市;
}
公共集getNetworks(){
返回网络;
}
公共网络(集合网络){
这个。网络=网络;
}
公共整数getStartYear(){
返回起始时间;
}
公共整数getEndYear(){
年底返回;
}
@凌驾
公共布尔等于(对象o){
if(this==o){
返回true;
}
如果(!(作业实例)){
返回false;
}
Job Job=(Job)o;
返回Objects.equals(position,job.position)&&
Objects.equals(日期、作业日期)&&
Objects.equals(城市、工作、城市);
}
@凌驾
公共int hashCode(){
返回Objects.hash(位置、日期、城市);
}
}
这是实际的作业类代码,也是我实现此操作的方式:

    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());
Map jobsMap=jobs.stream().collect(收集器.groupingBy(作业->作业));
jobsMap.keySet().stream()
.peek(job->jobsMap.get(job.stream().forEach(j->job.getNetworks().addAll(j.getNetworks()))
.sorted(Comparator.comparing((作业o)->Objects.firstNonNull(o.getEndYear(),Integer.MAX_值))
.reversed())
.collect(Collectors.toList());
但是我觉得这段代码很糟糕,特别是因为我在流中使用了外部映射,我想知道有没有办法在一个链中不用中间转换就能做到这一点。 如果对我实现此功能有任何合理的批评,我将不胜感激。
谢谢大家!

假设我们将所有
网络
合并到我们发现的特定
作业
第一次出现的中,我们可以在一行(相当复杂)中完成这项工作:

因此,这需要两个
Job
项,然后返回一个。这是一个折叠操作,因此
双功能
返回值一起依次返回到每个
作业
。我们所做的就是将新的
作业
中的所有
网络()添加到现有的
作业

reducing((l, r) -> {
    l.networks().addAll(r.networks());
    return l;
}
很明显,这会给您一个
映射
,但折叠这是一项简单的工作

我找不到一种方法可以直接将其放入
列表中


要将
映射
处理为
列表
,可以使用以下方法:

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);
List collect=jobs.stream()
.收集(分组方式(标识(),减少((l,r)->{
l、 网络().addAll(r.networks());
返回l;
})))
.values().stream()
.map(可选::get)
.收取(收费表);

尽管如此,它的可读性还是有问题。

您需要按
职位
分组
您的
工作
然后使用自定义收集器将
地图中的
列表
合并为单个
工作
。如果
城市
等其他字段不同,网络是否仍应合并到其他作业中?不,实际上,我是按相等分组方式(作业->作业)对对象进行分组的。Networks field没有参与equals。更新了我的答案,这样示例就不会混淆了。非常感谢!你能更新你的答案吗?首先,我们需要使用整个作业对象进行比较,而不是位置,其次,我们可以使用CollectingAnd来折叠选项和贴图。因此,这可以通过一个操作完成:
jobs.stream().collect(collectingAndThen(groupingBy(job->job,collectingAndThen)(reduce((l,r)->{l.getNetworks().addAll(r.getNetworks());return l;}),可选::get(),map->new arrararylist(map.values()))
@Skeeve你们每个人都能计算出这一行代码的作用吗?它已经很难辨认了,添加两个
收集,然后
操作使它完全难以辨认。。。例如,我建议
.values().stream().map(可选::get).collect(toList)
。因此,生成少量流不是一个坏做法吗?@skeev我认为在这种情况下,它增加了可读性——这应该是最重要的。使用
Stream
API,很容易被“单行”解决方案搞得有点神魂颠倒,结果是括号太多,以至于当你回到代码时,你根本不知道发生了什么。我不认为这个解决方案的性能会比在任何情况下都需要完成的大部分工作(创建
地图
列表
)是
收集然后
的方法差。OK。非常感谢你的帮助!