I have class Foo which has some field and I want to group them to Map<Object,List<Foo>>
by fields as follow:
class Foo {
private String cassette;
private List<String> organ; //not able to group on List<String>
private LocalDate date;
//getter setter toString
}
enum Group{
CASSETTE,
ORGAN,
DATE
}
I have a switch case for Grouping by java.util.function.Function
for Collectors.groupingBy()
as follow:
Function<Foo, Object> keyCriteria;
Group groupBy = Group.values()[1];//ordinal
switch (groupBy) {
case CASSETTE:
keyCriteria = p -> p.getCassette();
break;
case DATE:
keyCriteria = p -> p.getDate();
break;
case ORGAN:
keyCriteria = p -> p.getOrgan(); //facing problem while grouping with List<String>
break;
default:
keyCriteria = p -> p.getCassette();
}
Map<Object, List<Foo>> mapByCriteria = fooList.stream()
.collect(Collectors.groupingBy(keyCriteria));
System.out.println(mapByCriteria);
Everything works fine except case ORGAN:
Result getting:
{[Lung, Liver]=[Foo [cassette=1A, organ=[Lung, Liver], date=2020-01-13]], [Liver]=[Foo [cassette=2A, organ=[Liver], date=2020-01-15]]}
Expected result:
{Liver=[Foo [cassette=1A, organ=[Lung, Liver], date=2020-01-13], Foo [cassette=2A, organ=[Liver], date=2020-01-15]], Lung=[Foo [cassette=1A, organ=[Lung, Liver], date=2020-01-13]]}
Achieved expected result by work around following:
Map<Object, List<Foo>> collect = fooList.stream()
.flatMap(f -> f.getOrgan().stream().map(o -> new SimpleEntry<>(o, f))).collect(Collectors
.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
I'm looking for a switch
case and Generic
solution.
You can not handle fundamentally different operations, like a plain mapping and a flattening, with the same code. You have to handle the special case specially:
public static Map<String, List<Foo>> mapByCriteria(List<Foo> fooList, Group criteria) {
Function<Foo, String> simpleKeyCriteria;
switch(criteria) {
case CASSETTE: simpleKeyCriteria = Foo::getCassette; break;
case DATE: simpleKeyCriteria = p -> p.getDate().toString(); break;
case ORGAN:
return fooList.stream()
.flatMap(foo -> foo.getOrgan().stream()
.map(organ -> new AbstractMap.SimpleEntry<>(organ, foo)))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
default:
throw new AssertionError(criteria);
}
return fooList.stream().collect(Collectors.groupingBy(simpleKeyCriteria));
}
In principle, it would possible to share more common operations, by splitting the stream statement into parts, but it would produce even more code while the only common code is fooList.stream()
. So in this specific case, this is no win. But for completeness:
public static Map<String, List<Foo>> mapByCriteria(List<Foo> fooList, Group criteria) {
Stream<Foo> stream = fooList.stream(); // imagine more chained common operations
Function<Foo, String> simpleKeyCriteria = null;
Collector<Foo, ?, Map<String, List<Foo>>> collector = null;
switch(criteria) {
case CASSETTE: simpleKeyCriteria = Foo::getCassette; break;
case DATE: simpleKeyCriteria = p -> p.getDate().toString(); break;
case ORGAN: collector = Collectors.flatMapping(
foo -> foo.getOrgan().stream()
.map(organ -> new AbstractMap.SimpleEntry<>(organ, foo)),
Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
default:
throw new AssertionError(criteria);
}
if(collector == null) collector = Collectors.groupingBy(simpleKeyCriteria);
return stream.collect(collector);
}
This bears absolutely no code duplication, but as demonstrated, is not necessarily a win over accepting small code duplication.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.