Java 8 post grouping by -
i have list of objects of class job, every object has collection of tags (networks), collection mutable , has no impact on hashcode , objects equality.
what need list of unique job objects , each such object combine tags, example, have list:
[{position: "cto", dates: "2012-2014", city: "new york", networks: ["foo"]}, {position: "cto", dates: "2012-2014", city: "new york", networks: ["bar"]}]
this should reduced [{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); } }
this actual job class code, , how implemented operation:
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());
but feel bad code, since using external map inside stream , wonder there way in 1 chain without intermediate transformations. appreciate valid criticism implementation of functionality. thank you!
assuming merge networks
first occurrence of particular job
find, can in 1 (rather complex) line:
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; })));
i have used fluent accessors because cannot bothered type get
so. how work?
first stream
jobs
, call collectors.groupingby
on function.identity()
, gives map<job, list<job>>
.
but don't want list<job>
- collectors.reducing
comes in. passed downstream collector
of groupingby
.
the downstream collector
responsible creating value part of map
- in case reduce found jobs 1 job
.
reducing((l, r) -> { l.networks().addall(r.networks()); return l; }
so takes 2 job
items, , returns one. it's fold operation, bifunction
return
value in turn each job
. add networks()
new job
existing job
.
obviously gives map<string, optional<job>>
, collapsing simple job.
i cannot see way make list
directly...
in order process map<job, optional<job>>
list<job>
following can used:
collect.values().stream() .map(optional::get) .collect(tolist);
so, potentially whole lot in 1 line:
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);
although, readability of questionable.
Comments
Post a Comment