我有一个 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.groupingBy
上Function.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
项,并返回一项。这是一个折叠操作,所以 BiFunction
与return
值依次为每个 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/