Можно ли применить joiner (сборщик, аккумулятор) над функцией с помощью Java Guava?

Можно ли собирать строки поверх коллекции во время группировки? Вот как это работает в Java 8:

Map<String, String> discountOptions = p.getDiscountOptions().Stream()
    .collect(groupingBy(
        this::getDiscountName,
        Collectors.mapping(this::getValue, Collectors.joining(","))));

Мне любопытно, есть ли краткий способ сделать это в Google Guava? Вот как я пытаюсь повторить это в Гуаве:

Map<String, Collection<String>> stringCollectionMap = Multimaps.transformValues(
    Multimaps.index(p.getDiscountOptions(), 
        new Function<DiscountOption, String>() {
            @Override
            public String apply(DiscountOption d) {
                return getDiscountName(d);
            }
        }),
    new Function<DiscountOption, String>() {
        @Override
        public String apply(DiscountOption d) {
            return getValue(d);
        }
    }).asMap();

Map<String, String> discountOptions =  Maps.transformValues(
    stringCollectionMap,
    new Function<Collection<String>, String>() {
        @Override
        public String apply(Collection<String> strings) {
            return Joiner.on(",").join(strings);
        }
    });

1 ответ

Решение

Вы не получите ничего более краткого, чем API потоков Java 8, поскольку причина, по которой он существует, заключается в улучшении такого рода операций.

Функциональное программирование до Java 8 может быть связано с функциональными утилитами Guava, но, как они предупреждают:

Начиная с Java 7, функциональное программирование в Java может быть аппроксимировано только посредством неуклюжего и многословного использования анонимных классов... Чрезмерное использование функциональных идиом программирования Guava может привести к многословному, запутанному, нечитаемому и неэффективному коду... Императивный код должен быть по умолчанию, ваш первый выбор с Java 7.

Вот обязательный перевод вашего кода:

private static final Joiner COMMA_JOINER = Joiner.on(",");

ListMultimap<String, String> groupedByDiscountName = ArrayListMultimap.create();
for (DiscountOption option : p.getDiscountOptions()) {
  groupedByDiscountName.put(getDiscountName(option), getValue(option));
}

ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (Entry<String, Collection<String>> e : groupedByDiscountName.asMap().entrySet()) {
  builder.put(e.getKey(), COMMA_JOINER.join(e.getValues());
}
Map<String, String> discountOptions = builder.build();

Это короче и легче для чтения. Учитывая ваш текущий API, это примерно лучшее, что вы можете сделать с Java 7.

Тем не менее, вы можете рассмотреть возможность пересмотра своего API - в частности, странно, что вы используете статические методы (getDiscountName(), getValue()) чтобы извлечь данные из вашего DiscountOption класс - они кажутся очевидными кандидатами на методы экземпляра. Точно так же вы могли бы рассмотреть возможность создания Discounts класс, который содержит один или несколько DiscountOption экземпляры и обеспечивает toString() который возвращает вашу строку через запятую. Тогда вам просто нужно построить свой Discounts объекты, и вы хорошо идти.

Другие вопросы по тегам