Reputation: 51
I have
List<Gift> gifts = new ArrayList<>();
gifts .add(new Gift().withType(INF, CHD));
gifts .add(new Gift().withType(ADT, CHD));
gifts .add(new Gift().withType(INF, ADT));
Gift has a method List<Type> getTypes()
;
and now I'd like to transform gifts list into something like
Map<Type,List<Gift>>
.
I'd like to do it with Java 8 and lambdas in one line. Is it possible?
public class Gift {
public List<Type> getTypes() {
return types;
}
public Gift withType(Type... types) {
this.types = Arrays.asList(types);
return this;
}
List<Type> types = new ArrayList<>();
}
public enum Type {
ADT,
CHD,
INF;
}
Previous old code (it looks awfully). That's all what I have.
Map<Type, List<Gift>> byTypes = new HashMap<>();
for (Gift gift : gifts) {
for (Type type : gift.getTypes()) {
List<Gift> giftList = byTypes.get(type);
if (giftList == null) {
giftList = new ArrayList<>();
}
giftList.add(gift);
byTypes.put(type,giftList);
}
}
Upvotes: 4
Views: 160
Reputation: 51
I found other solution with a reduce method. In my opinion smaller one and easier to understand.
HashMap<Type, List<Gift>> result = gifts.stream().reduce(
new HashMap<Type, List<Gift>>() {{
asList(Type.values()).stream().forEach(type -> put(type, new ArrayList<>()));
}}
,
(map, gift) -> {
gift.getTypes().stream().forEach(type -> map.get(type).add(gift));
return map;
}
,
(map1, map2) -> {
asList(Type.values()).stream().forEach(type -> map1.get(type).addAll(map2.get(type)));
return map1;
}
);
Upvotes: 0
Reputation: 9405
Reintroducing your intermediate Pair class P
, but in a slightly different way. I think we might expect that in later iterations of Java, this is going to become much more common place and easier to do with the introduction of a built-in Pair type, and the implementation of Value Objects.
package play;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static play.Type.*;
public class Play {
static class P<K,V> { K k; V v; P(K kk,V vv) {k=kk;v=vv;}}
public static void main(String[] args) throws IOException {
List<Gift> gifts = new ArrayList<>();
gifts .add(new Gift().withType(INF, CHD));
gifts .add(new Gift().withType(ADT, CHD));
gifts .add(new Gift().withType(INF, ADT));
Map<Type,List<Gift>> m = gifts.stream()
.flatMap((g)->g.getTypes().stream().map((t)->new P<>(t,g)))
.collect(
Collectors.groupingBy(
(p)->p.k,
// Does not work with <code>new EnumMap<>(Type.class)</code>
// I dont know why, ...
()->new EnumMap(Type.class),
Collectors.mapping(
(p)->p.v,
Collectors.toList()
)
)
);
System.out.println("Map: "+m.toString());
}
}
My only problem in this code is that I cannot explain the need for a 'Untyped' Map as the Map factory. Feel free to explain if you know why ...
Upvotes: 0
Reputation: 51
Ok, I found solution which satify me :-) I wrote my collector :-D
Map<Type, List<Gift>> collect1 = gifts.stream().collect(new TypeToManyGiftCollector());
public class TypeToManyGiftCollector
implements Collector<Gift, Map<Type, List<Gift>>, Map<Type, List<Gift>>> {
@Override
public Supplier<Map<Type, List<Gift>>> supplier() {
return () -> new HashMap<Type, List<Gift>>() {{
for (Type type : Type.values()) {
put(type, new ArrayList<Gift>());
}
}};
}
@Override
public BiConsumer<Map<Type, List<Gift>>, Gift> accumulator() {
return (Map<Type, List<Gift>> map, Gift gift) -> {
gift.getTypes().stream().forEach(type -> map.get(type).add(gift));
};
}
@Override
public BinaryOperator<Map<Type, List<Gift>>> combiner() {
return (Map<Type, List<Gift>> map1, Map<Type, List<Gift>> map2) ->
{
for (Type type : Type.values()) {
map1.get(type).addAll(map2.get(type));
}
return map1;
};
}
@Override
public Function<Map<Type, List<Gift>>, Map<Type, List<Gift>>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
}
Upvotes: 1