Сложение BigDecimals через Java Streams: точность и ограничения
Быстрый ответ
Для суммирования значений BigDecimal с помощью Stream API удобно использовать метод reduce():
BigDecimal sum = bigDecimals.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
Переменная bigDecimals содержит вашу коллекцию объектов типа BigDecimal. Метод stream() преобразует ее в поток, а reduce() выполняет непосредственное суммирование, принимая за начальное значение BigDecimal.ZERO и используя метод BigDecimal::add для накопления результата.

Операционные сценарии
Если вам приходилось выполнять сложные вычисления или работать с сложными объектами, вы оцените простоту решения, предложенного Java. это сочетание методов map и reduce. Например, когда перед вами стоит задача рассчитать общую сумму счетов-фактур:
// Метод подсчета итоговой суммы счетов-фактур
BigDecimal invoiceTotalSum = invoices.stream()
.map(Invoice::total)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Данный код конвертирует каждый счет-фактуру в денежную сумму с помощью метода Invoice::total, а затем суммирует эти суммы. Для обхода проблем с null, можно добавить фильтрацию:
// Фильтрация null-значений перед суммированием
BigDecimal invoiceTotalSum = invoices.stream()
.filter(Objects::nonNull)
.map(Invoice::total)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Применение метода filter(Objects::nonNull) исключает null значения из потока перед остальными действиями. Это поможет избежать исключения NullPointerException.
Обеспечение точности
Для анализа эффективности кода очень полезным оказываются статистические данные. Eclipse Collections предлагает функцию summarizingBigDecimal():
// Статистические данные по работе с BigDecimal
MutableBigDecimalSummaryStatistics stats =
bigDecimals.stream()
.collect(Collectors2.summarizingBigDecimal(Function.identity()));
MutableBigDecimalSummaryStatistics предоставляет обширный набор статистических данных, включая общую сумму, среднее значение, количество, минимальное и максимальное значения. Важно применять методы с BigDecimal, не приводя этот тип к double или float, чтобы обеспечить высокую точность выполняемых вычислений.
Визуализация
Процесс суммирования значений BigDecimal можно визуализировать, представив дельфинов, переносящих раковины (которые представляют BigDecimal):
🐬(👜🐚) -> 🐬(👜🐚) -> 🐬(👜🐚) -> ... -> 🐋(💼🐚)
Наша цель – сложить все раковины и передать их киту:
// Суммирование раковин у дельфинов
BigDecimal total = dolphins.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
На выходе получается кит, который носит сумку с общим числом раковин (💼🐚). Это соответствует сумме BigDecimal.
Настройка параметров
Тщательная настройка поведения при работе со значениями BigDecimal может быть важной для обеспечения точности и производительности. Вам может пригодиться пользовательский Collector:
// Настраиваем персонализированный Collector
Collector<BigDecimal, ?, BigDecimal> summingUp =
Collector.of(
() -> new BigDecimal[]{BigDecimal.ZERO},
(a, t) -> a[0] = a[0].add(t),
(a, b) -> new BigDecimal[]{a[0].add(b[0])},
a -> a[0],
Collector.Characteristics.IDENTITY_FINISH
);
Пользовательский Collector, получивший название summingUp, создан особенно для обеспечения точности при накоплении значений.
Защита от скрытых ловушек
Также важно обезопасить свои вычисления от ошибок, вызванных null-значениями:
// Защита от null-значений
Function<Invoice, BigDecimal> safeInvoiceTotal = invoice ->
(invoice != null && invoice.getUnitPrice() != null && invoice.getQuantity() != null)
? invoice.total()
: BigDecimal.ZERO;
BigDecimal safeSum = invoices.stream()
.map(safeInvoiceTotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Функция safeInvoiceTotal обеспечивает надежную защиту от проблем, связанных с нелевыми значениями, в вашем коде.
Полезные материалы
- BigDecimal (Java Platform SE 8) — детальная документация
BigDecimal. - Java 8 Stream Tutorial – winterbe — обучающий материал по работе с Streams в Java 8.
- Reduction (The Java™ Tutorials) — подробно о сокращающих операциях в Java Streams.
- Java Practices->Representing money — описание и особенности работы с денежными суммами в
BigDecimal. - c# – is there anyway this LINQ could end up doing too much work? — обсуждение эффективности операций в потоках, например, с
BigDecimal.


