2017-02-22 9 views
7

私はList<LedgerEntry> ledgerEntriesを持っており、creditAmountとdebitAmountの合計を計算する必要があります。Java 8 1回の繰り返しで2つのオブジェクトプロパティを合計する

class LedgerEntry{ 
private BigDecimal creditAmount; 
private BigDecimal debitAmount; 

//getters and setters 
} 

私は二回リストを反復てるように見えます。これは、よう

BigDecimal creditTotal = ledgeredEntries.stream().map(p ->p.getCreditAmount()). 
reduce(BigDecimal.ZERO, BigDecimal::add); 
BigDecimal debitTotal = ledgeredEntries.stream().map(p ->p.getDebitAmount()). 
reduce(BigDecimal.ZERO, BigDecimal::add); 

//... 
//Use creditTotal, debitTotal later 

これを実装しました。リストを2回スチームしなくても、これを1回で済ませる方法はありますか?それは正しくそのreduce()を指摘されたコメントで

LedgerEntry totalsEntry = entries.stream().reduce(new LedgerEntry(), (te, e) -> { 
    te.setCreditAmount(te.getCreditAmount().add(e.getCreditAmount())); 
    te.setDebitAmount(te.getDebitAmount().add(e.getDebitAmount())); 

    return te; 
}); 

更新

あなたは合計エントリに減らすことができる

前のJava 8バージョン

BigDecimal creditTotal = BigDecimal.ZERO; 
BigDecimal debitTotal = BigDecimal.ZERO; 
for(LedgerEntry entry : ledgerEntries){ 
    creditTotal = creditTotal.add(entry.getCreditAmount()); 
    debitTotal = debitTotal.add(entry.getDebitAmount()); 
} 
+3

あなたはストリームを使用したいのはなぜ?あなたの "Pre Java 8"バージョンも100%有効なJava 8であり( 'BigDecimal'は不変であるため実際に何もしないという事実のために修正された時)、どんなストリームよりも読みやすく保守性があります一度に2つの合計を計算しようとする解。 – Hoopje

+0

@KrazyKalle:ありがとう。編集しました – Krishan

+0

@KrazyKalle。はい。あなたは括弧の間にある文で何を意味すると思いますか? – Hoopje

答えて

12

初期識別子を変更すべきではない変更可能な縮小にはcollect()を使用する必要があります。以下はcollect()を使用するバージョンです(アキュムレータとコンバイナの両方に同じBiConsumerを使用しています)。また、creditAmountおよび/またはdebitAmountの値が設定されていない場合、潜在的なNPEの問題も解決します。

BiConsumer<LedgerEntry, LedgerEntry> ac = (e1, e2) -> { 
    BigDecimal creditAmount = e1.getCreditAmount() != null ? e1.getCreditAmount() : BigDecimal.ZERO; 
    BigDecimal debitAmount = e1.getDebitAmount() != null ? e1.getDebitAmount() : BigDecimal.ZERO; 

    e1.setCreditAmount(creditAmount.add(e2.getCreditAmount())); 
    e1.setDebitAmount(debitAmount.add(e2.getDebitAmount())); 
}; 

LedgerEntry totalsEntry = entries.stream().collect(LedgerEntry::new, ac, ac); 

突然、Java 8以前のバージョンが魅力的に見えるようになりました。

+1

可読性のために 'new LedgerEntry()'を 'LedgerEntry :: new'に置き換えることができますか? – CKing

+3

@CKingいいえ、初期値です。メソッド参照ではありません。 –

+0

注目。私は 'reduce'は' Supplier'のような機能的インターフェースをとるオーバーロードされたフォームを持っていると思っていましたが、そのようなものはないと思います。 – CKing

1

あなたはいくつかの並べ替えのPairにあなたの結果をラップする必要があります。

stream 
     .parallel() 
     .reduce(new AbstractMap.SimpleEntry<>(BigDecimal.ZERO, BigDecimal.ZERO), 
        (entry, ledger) -> { 
         BigDecimal credit = BigDecimal.ZERO.add(entry.getKey()).add(ledger.getCreditAmount()); 
         BigDecimal debit = BigDecimal.ZERO.add(entry.getValue()).add(ledger.getDebitAmount()); 
         return new AbstractMap.SimpleEntry<>(credit, debit); 
        }, (left, right) -> { 
         BigDecimal credit = BigDecimal.ZERO.add(left.getKey()).add(right.getKey()); 
         BigDecimal debit = BigDecimal.ZERO.add(left.getValue()).add(right.getValue()); 
         return new AbstractMap.SimpleEntry<>(credit, debit); 
        })); 
関連する問題