2014-07-04 20 views
5

我有下面的類:總結列表<X>列出<X>與Java 8個流API

class Money { 
    CurrencyUnit currencyUnit; 
    BigDecimal amount; 
} 

在我的申請,我得到Money對象的一些隨機列表:

currencyUnit | amount 
--------------------- 
EUR   | 5.1 
EUR   | 0 
USD   | 1.09 
EUR   | 42 
USD   | 3 

現在我想使用Java 8 Stream API創建以下結果(僅針對每個貨幣單位的金額調用BigDecimal::add):

currencyUnit | amount 
--------------------- 
EUR   | 47.1 
USD   | 4.09 

我已經知道/ DID:

Stream<Money> moneyStream = moneyList.stream(); 

這裏,它已經結束。我知道我可以使用一個Collector產生Map<CurrencyUnit, List<Money>>

moneyStream.collect(Collectors.groupingBy(m -> m.getCurrencyUnit()); 

但後來我還是要經過的所有鍵值對,並總結量數據。

什麼是這樣做的(也可能是最簡單的方法)?它不可能那麼複雜,對吧? :)


編輯:如果它不是那麼清楚我需要什麼,這是我的舊路Java代碼:

Map<CurrencyUnit, Money> map = new HashMap<>(); 
moneyList.stream().forEach(e -> { 
    Money m = map.get(e.getCurrencyUnit()); 
    if(m == null) { 
     m = new Money(); 
     m.setAmount(BigDecimal.ZERO); 
     m.setCurrencyUnit(e.getCurrencyUnit()); 
     map.put(e.getCurrencyUnit(), m); 
    } 
    m.setAmount(m.getAmount().add(e.getAmount())); 
}); 
return map.values(); 

編輯2:另一種解決方案,這是不是真的優雅:

List<Money> list = inputList.stream() 
    .collect(Collectors.groupingBy(Money::getCurrencyUnit)) 
    .values().stream().map(ml -> { 
     Money money = new Money(); 
     ml.forEach(m -> { 
      if(money.getCurrencyUnit() == null) { 
       money.setCurrencyUnit(m.getCurrencyUnit()); 
       money.setAmount(m.getAmount()); 
      } else { 
       money.setAmount(money.getAmount().add(m.getAmount())); 
      } 
     }); 
     return money; 
    }).collect(Collectors.toList()); 

回答

10

您可以通過CurrencyUnit使用groupingBy收集到組的對象。沒有第二個參數,groupingBy方法將元素收集到列表中。但是,如果您需要其他內容,您還可以指定下游收集器

您可以使用Collectors::summingIntCollectors::summingLongintlong。對於BigDecimal,你可以回退到Collectors::reducing

import static java.util.stream.Collectors.groupingBy; 
import static java.util.stream.Collectors.reducing; 

Map<CurrencyUnit, BigDecimal> result = moneyList.stream() 
    .collect(
     groupingBy(
      Money::getCurrencyUnit, 
      reducing(BigDecimal.ZERO, Money::getAmount, BigDecimal::add))); 

編輯:您還可以創建List<Money>

List<Money> result = moneyList.stream() 
    .collect(
     groupingBy(
      Money::getCurrencyUnit, 
      reducing(BigDecimal.ZERO, Money::getAmount, BigDecimal::add))) 
    .entrySet().stream() 
    .map(e -> new Money(e.getKey(), e.getValue()) 
    .collect(toList()); 
+0

謝謝。這與我所需要的很接近,但是你的代碼給了我一個'Map'。是不是有可能找回一個'List'或者'Set'? (沒有手動創建新的Money對象等) –

+0

就在5分鐘前,迭代entrySet的想法出現在我的腦海中,但我想:這真的很醜,必須有更性感/優雅的東西。 '---'但我認爲你給出的答案是這樣做的唯一方法。 ' - '現在我想:'moneyStream.collect(Collectors.groupingBy(m - > m.getCurrencyUnit());'然後用map生成'Map >'。 values()。stream()'產生一個'List '?! –