Подтвердить что ты не робот

Лучший способ объединения с Java 8

У меня есть следующий код, который я пытаюсь улучшить:

BigDecimal total = entity.getAssociate().stream().map(Associates::getPropertyA)
    .reduce(BigDecimal.ZERO, BigDecimal::add);
total = entity.getAssociate().stream().map(Associates::getPropertyB)
    .reduce(total, BigDecimal::add);
total = entity.getAssociate().stream().map(Associates::getPropertyC)
    .reduce(total, BigDecimal::add);
total = entity.getAssociate().stream().map(Associates::getPropertyD)
    .reduce(total, BigDecimal::add);

Это работает, но действительно кажется, что есть лучший способ сделать это. Может ли кто-нибудь просветить меня по этому вопросу?

4b9b3361

Ответ 1

Если все эти свойства одного типа (кажется, что они все BigDecimal), вы можете использовать flatMap для создания единственного Stream из них всех, а затем reduce его к общей сумме:

BigDecimal total = 
    entity.getAssociate()
          .stream()
          .flatMap (a -> Stream.of(a.getPropertyA(),a.getPropertyB(),a.getPropertyC(),a.getPropertyD()))
          .reduce(BigDecimal.ZERO, BigDecimal::add);

Ответ 2

Вы можете просто добавить все свойства внутри карты:

BigDecimal total = entity.getAssociate().stream()
            .map(a -> a.getPropertyA()
                    .add(a.getPropertyB())
                    .add(a.getPropertyC())
                    .add(a.getPropertyD()))
            .reduce(BigDecimal.ZERO, BigDecimal::add);

Помните, что это изменяет порядок добавления чисел.

Ответ 3

Если вы можете добавить следующий класс в класс Associates:

public BigDecimal getSubtotal() {
    return propertyA.add(propertyB).add(propertyC).add(propertyD);
}

Тогда выполнение задачи будет легким:

BigDecimal total = entity.getAssociate().stream()
    .map(Associate::getSubtotal)
    .reduce(BigDecimal::add)
    .orElse(BigDecimal.ZERO);

Ответ 4

Слова "лучше" или "лучше" должны относиться к некоторой метрике. Представление? Читаемость? Элегантность?

В ответе Eran показан один подход, а именно создание небольших потоков, содержащих значения свойств A, B, C и D для каждого ассоциированного объекта, и плоское отображение этих значений в более крупный поток. Порядок суммирования в этом подходе равен

A0 + B0 + C0 + D0  +  A1 + B1 + C1 + D1  + ... +  An + Bn + Cn + Dn

Другой вариант - создать отдельные потоки свойств A, B, C и D и объединить эти потоки перед применением сокращения. Это можно сделать с помощью вложенных вызовов Stream#concat, но более элегантно и гибко используя flatMap с функцией идентификации:

Stream<BigDecimal> stream = Stream.of(
    entity.getAssociate().stream().map(Associates::getPropertyA),
    entity.getAssociate().stream().map(Associates::getPropertyB),
    entity.getAssociate().stream().map(Associates::getPropertyA),
    entity.getAssociate().stream().map(Associates::getPropertyC))
    .flatMap(Function.identity());

BigDecimal total = stream.reduce(BigDecimal.ZERO, BigDecimal::add);

Ключевым моментом является то, что в этом случае порядок суммирования

A0 + A1 + ... + An + B0 + B1 + ... + Bn + C0 + C1 + ... + Cn 

(Это может не иметь особого значения. Но это подход, который концептуально отличается от тех, которые были предложены до сих пор (с точки зрения порядка суммирования) и, следовательно, можно упомянуть в качестве одного из вариантов - Кроме того, он более похож на подход, который вы используете в настоящее время, но без разбитого значения идентичности для сокращения)

Ответ 5

Или просто a forEach:

BigDecimal[] total = new BigDecimal[] { BigDecimal.ZERO };
entity.getAssociate().stream().forEach(a -> {
    total[0] = total[0].add(a.getPropertyA());
    // ... and so on for all others
});

Как сторона - не ваша текущая реализация неверна, так как вы нарушаете identity сокращения.