Собрать groupBy по глубокому свойству - PullRequest
0 голосов
/ 26 сентября 2018
private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
    if (squares == null || squares.isEmpty()) {
        return emptyMap();
    }

    Map<String, Set<Square>> res = new HashMap<>();

    squares.stream()
        .filter(square -> {
            if (square.getZuloCodes().isEmpty()) {
                LOG("Ignored {}", square.id);
                return false;
            }
            return true;
        })
        .forEach(square -> {
          square.getZuloCodes()
            .forEach(code -> {
                res.putIfAbsent(code, new HashSet<>());
                res.get(code).add(square);
            }));
        });

    return Collections.unmodifiableMap(res);
}

Код выше получает список квадратов, и эти квадраты могут содержать ZuloCodes внутри.Выходными данными должны быть неизменяемые значения zuloCode Map и значение всех квадратов с этим UniquePrefix.Как вы можете видеть, я не могу найти способ удалить вспомогательную коллекцию res и сделать код легко читаемым, есть ли способ разбить эту коллекцию на [zuloCode, square] и затем collect.groupBy?Кроме того, что если внутри фильтра не читается, как бы вы справились с этим?

Ответы [ 2 ]

0 голосов
/ 26 сентября 2018

Стандартный подход использует flatMap перед сбором, используя groupingBy, но, поскольку вам нужен исходный Square для каждого элемента, вам необходимо отобразить объект, содержащий и экземпляр Square, и код zuloString.

Поскольку в Java нет стандартной пары или типа кортежа (пока), обходной путь - использовать экземпляр Map.Entry, например

private Map<String, Set<Square>> populateZuloSquare0(List<Square> squares) {
    if (squares == null || squares.isEmpty()) {
        return emptyMap();
    }
    return squares.stream()
        .filter(square -> logMismatch(square, !square.getZuloCodes().isEmpty()))
        .flatMap(square -> square.getZuloCodes().stream()
            .map(code -> new AbstractMap.SimpleEntry<>(code, square)))
        .collect(Collectors.collectingAndThen(
            Collectors.groupingBy(Map.Entry::getKey,
                Collectors.mapping(Map.Entry::getValue, Collectors.toSet())),
            Collections::unmodifiableMap));
}
private static boolean logMismatch(Square square, boolean match) {
    if(!match) LOG("Ignored {}", square.id);
    return match;
}

Anальтернативой является использование настраиваемого сборщика, который будет перебирать ключи:

private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
    if (squares == null || squares.isEmpty()) {
        return emptyMap();
    }
    return squares.stream()
        .filter(square -> logMismatch(square, !square.getZuloCodes().isEmpty()))
        .collect(Collector.of(
            HashMap<String, Set<Square>>::new,
            (m,square) -> square.getZuloCodes()
                .forEach(code -> m.computeIfAbsent(code, x -> new HashSet<>()).add(square)),
            (m1,m2) -> {
                if(m1.isEmpty()) return m2;
                m2.forEach((key,set) ->
                    m1.merge(key, set, (s1,s2) -> { s1.addAll(s2); return s1; }));
                return m1;
            },
            Collections::unmodifiableMap)
        );
}

Обратите внимание, что этот настраиваемый сборщик можно рассматривать как вариант параллельного кода следующего цикла:

private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
    if (squares == null || squares.isEmpty()) {
        return emptyMap();
    }
    Map<String, Set<Square>> res = new HashMap<>();
    squares.forEach(square -> {
        if(square.getZuloCodes().isEmpty()) LOG("Ignored {}", square.id);
        else square.getZuloCodes().forEach(
            code -> res.computeIfAbsent(code, x -> new HashSet<>()).add(square));
    });
    return Collections.unmodifiableMap(res);
}

что может показаться не таким уж плохим сейчас, когда вам не нужен код для параллельной работы…

0 голосов
/ 26 сентября 2018

Как насчет этого.Вы можете использовать операцию map merge, чтобы выполнить эту задачу.Я обновил фильтр и тоже упростил его.

squares.stream().filter(s -> !s.getZuloCodes().isEmpty())
    .forEach(s -> s.getZuloCodes().stream().forEach(z -> res.merge(z, new HashSet<>(Arrays.asList(s)),
        (s1, s2) -> Stream.of(s1, s2).flatMap(Collection::stream).collect(Collectors.toSet()))));
...