Поток не работает в цикле for

BigDecimal getInterest(List<Investment> investments) {
        BigDecimal interest = BigDecimal.ZERO;
        for (Investment i: investments) {
            i.getTransactions().stream()
                    .map(Transaction::getAmount)
                    .forEach(interest::add);
        }
        return interest;
    }

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

BigDecimal getInterest(List<Investment> investments) {
        BigDecimal interest = BigDecimal.ZERO;
        for (Investment i: investments) {
            interestPaid = interest.add(i.getTransactions().stream()
                    .map(Transaction::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
        }
        return interest;
    }

person Virx    schedule 07.09.2016    source источник
comment
Может быть, потому что BigDecimal неизменяем, поэтому он возвращает новый экземпляр при вызове добавления вместо обновления исходного?   -  person Koekje    schedule 07.09.2016
comment
@Eran: Это BigDecimal, вот в чем проблема.   -  person fabian    schedule 07.09.2016
comment
@fabian Я пропустил это, спасибо. На самом деле это BigDecimal, но та же проблема.   -  person Eran    schedule 07.09.2016
comment
Возможный дубликат сложения BigDecimals с использованием потоков   -  person Didier L    schedule 07.09.2016


Ответы (3)


BigDecimal неизменяем, поэтому ваш forEach вызывает add, но ничего не делает с результатом. В данном случае reduce является правильным оператором потока.

Если вы видите Сложение BigDecimals с помощью потоков. Вы должны использовать .reduce(BigDecimal.ZERO, BigDecimal::add), что сделает ваше тело цикла:

interest = i.getTransactions().stream()
                .map(Transaction::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
person davidsheldon    schedule 07.09.2016

Поскольку BigDecimal является неизменяемым, вызов add для него не изменит значение. Вместо этого он возвращает новый BigDecimal. forEach просто игнорирует любые возвращенные значения.

interest всегда будет сохранять исходное значение BigDecimal.ZERO.

В отличие от этого reduce объединяет элементы, используя данный BinaryOperator. BigDecimal::add на самом деле является сокращенной формой для (a, b) -> a.add(b), и этот оператор будет применяться для объединения всех элементов потока.

person fabian    schedule 07.09.2016

Как объясняет принятый ответ, BigDecimal.add не изменяет экземпляр, а возвращает новое значение, и ваш первый вариант не использует результат. В качестве дополнения, то же самое произойдет, если вы не используете результат reduce во втором варианте, но там вы не только передаете результат вызову add для предыдущего значения, вы также хотите присвоить результат этот последующий add в вашу локальную переменную.

Но стоит отметить, что ваша смесь циклической и потоковой операций несовместима. Вы можете выразить всю операцию как одну операцию Stream:

BigDecimal getInterest(List<Investment> investments) {
    return investments.stream()
        .flatMap(i -> i.getTransactions().stream())
        .map(Transaction::getAmount)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
}
person Holger    schedule 07.09.2016