Сложение 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
.