Generi c java .util.function. Функция для группировки по входу - PullRequest
0 голосов
/ 20 февраля 2020

У меня есть класс Foo с некоторым полем, и я хочу сгруппировать их в Map<Object,List<Foo>> по следующим полям:

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
}

У меня есть регистр переключения для группировки по java.util.function.Function для Collectors.groupingBy() следующим образом:

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);

Все отлично работает, кроме case ORGAN:

Результат получен:

{[Lung, Liver] = [Foo [cassette = 1A , орган = [Легкие, Печень], дата = 2020-01-13]], [Печень] = [Foo [кассета = 2A, орган = [Печень], дата = 2020-01-15]]}

Ожидаемый результат:

{Печень = [Foo [кассета = 1A, орган = [Легкие, печень], дата = 2020-01-13], Foo [кассета = 2A, орган = [печень], дата = 2020-01-15]], легкое = [Foo [кассета = 1A, орган = [легкое, печень], дата = 2020-01-13]]}

Достигнут ожидаемый результат благодаря обходу следующего:

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())));

Я ищу switch кейс и Generic решение.

1 Ответ

3 голосов
/ 20 февраля 2020

Вы не можете обрабатывать принципиально разные операции, такие как простое отображение и выравнивание, с одним и тем же кодом. Вы должны обрабатывать особый случай специально:

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));
}

В принципе, можно разделить более общие операции, разделив оператор потока на части, но это приведет к еще большему количеству кода, в то время как единственный общий код это fooList.stream(). Так что в данном конкретном случае это не победа. Но для полноты:

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);
}

Это не имеет абсолютно никакого дублирования кода, но, как продемонстрировано, не обязательно является победой над принятием небольшого дублирования кода.

...