Java8POST分组方法
我有一个Job类的对象列表,每个对象都有一个标记集合(网络),这个集合是可变的,对hashCode和对象的平等性没有影响 我需要做的是获得所有唯一作业对象的列表,并为每个此类对象组合所有标记,例如,我有一个列表: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
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。非常感谢你的帮助!