Reputation: 2761
I have this class structure:
public class A {
private List<B> bs;
...//getters
}
public class C {
private Long id;
...//getters
}
public class B {
private Long idOfC;
...//more stuff
}
B::getIdOfC matches C::getId
In a better design B would just contain a reference to C, rather than its id (I cannot change that), so that's why now I need to create a map, so my method signature looks like this
public Map<A, List<C>> convert(Collection<A> collection)
Inside this convert method, there is a
List<C> getCsByIds(List<Long> id)
that's later used to match it against B.idOfC, but there should only be one call to this method as it's pretty expensive.
So If I go like this:
List<B> bs = Arrays.asList(new B(10L), new B(11L)); //10L and 11L are the values of idOfC
List<A> as = Arrays.asList(bs);
//And assuming getCsByIds returns Arrays.asList(new C(10L), new C(11L), new C(12L));
then
Map<A, List<C>> map = convert(as);
map.values().get(0)
returns something like Arrays.asList(new C(10L), new C(11L))
The method that does this is pretty massive in my view:
public Map<A, List<C>> convert(Collection<A> as) {
List<Long> cIds = as.stream()
.flatMap(a -> a.getBs().stream())
.map(B::getCId)
.collect(Collectors.toList());
//single call to gsCsByIds
Map<Long, C> csMap = getCsByIds(cIds)
.stream()
.collect(Collectors.toMap(C::getId, Function.identity()));
//a whole new map is created by iterating over the list called "as"
Map<A, List<C>> csByAs = new HashMap<>();
if (!csMap.isEmpty()) {
for (A a : as) {
Set<C> cs = getCsFromMap(csMap, a.getBs());
if (!cs.isEmpty()) {
csByAs.put(a, new ArrayList<>(cs));
}
}
}
return csByAs;
}
private Set<B> getCsFromMap(Map<Long, C> cMap, List<B> bs) {
return bs.stream()
.map(b -> cMap.get(b.getIdOfc()))
.collect(Collectors.toSet());
}
Is there a way to make this simpler???
Upvotes: 3
Views: 174
Reputation: 31868
If the call to getCsByIds
is expensive, your initial idea is pretty decent to execute itself. It can further be cut short to :
public Map<A, List<C>> convert(Collection<A> as) {
List<Long> cIds = as.stream()
.flatMap(a -> a.getBs().stream())
.map(B::getIdOfC)
.collect(Collectors.toList());
Map<Long, C> csMap = getCsByIds(cIds).stream()
.collect(Collectors.toMap(C::getId, Function.identity()));
return as.stream()
.collect(Collectors.toMap(Function.identity(),
a -> a.getBs().stream().map(b -> csMap.get(b.getIdOfC()))
.collect(Collectors.toList()), (a, b) -> b));
}
where you can choose your merge function (a,b) -> b
accordingly.
Upvotes: 2
Reputation: 186
Maybe just iterate over the As directly? (no compiler at hand, so maybe the snippet is not compile-ready)
public Map<A, List<C>> convert(Collection<A> as) {
Map<A, List<C>> result = new HashMap<>();
for(A a: as){
List<Long> cIds = a.getBs().stream()
.map(B::getIdOfC)
.collect(Collectors.toList());
result.put(a, getCsByIds(cIds));
}
return result;
}
Upvotes: 1
Reputation: 21975
Wouldn't something like this work? I don't have a compiler so I can't really test it
public Map<A, List<C>> convert(Collection<A> as) {
return as.stream()
.collect(Collectors.toMap(Function::identity,
a -> a.getBs().stream()
.map(B::getIdOfC)
.flatMap(id -> getCsByIds(asList(id))
.values()
.stream())
.collect(Collectors.toList())
)
);
}
Upvotes: 0