Java 8 Stream从对象映射创建对象

时间:2018-09-01 08:17:01

标签: java java-8 java-stream

我刚开始通过Java 8流API学习和实现集合。我有一堂课:

public class Discount {
    int amount;
    String lastMarketingRegion;

    Discount (int amount, String lastMarketingRegion) {
        this.amount = amount;
        this.lastMarketingRegion= lastMarketingRegion;
    }

    public int getAmount() { return amount; }

    public String getLastMarketingRegion() { return lastMarketingRegion; }

    public String toString() {
        return String.format("{%s,\"%s\"}", amount, lastMarketingRegion);
    }
}

给出以下内容:

Map<String, Discount> prepaid = new HashMap<String, Discount>();
prepaid.put("HAPPY50", new Discount(100, "M1"));
prepaid.put("LUCKY10", new Discount(10, "M2"));
prepaid.put("FIRSTPAY", new Discount(20, "M3"));

Map<String, Discount> otherBills = new HashMap<String, Discount>();
otherBills.put("HAPPY50", new Discount(60, "M4"));
otherBills.put("LUCKY10", new Discount(7, "M5"));
otherBills.put("GOOD", new Discount(20, "M6"));

List<Map<String, Discount>> discList = new ArrayList<Map<String, Discount>>();
discList.add(prepaid);
discList.add(otherBills);

因此,基本上,我有一个Discount地图列表,其中列出了不同付款方式的所有折扣代码。

要求是使用sum_of_amountlast_region创建一个具有所有付款类型的所有折扣代码的地图:

Map<String, Discount> totalDiscounts = 
{LUCKY10={17, "M5"}, FIRSTPAY={20, "M3"}, HAPPY50={160, "M4"}, GOOD={20, "M6"}}

我能够得到:

Map<String, Integer> totalDiscounts = 
    {LUCKY10=17, FIRSTPAY=20, HAPPY50=160, GOOD=20}

通过使用以下代码:

 Map<String, Integer> afterFormatting = discList.stream()
                           .flatMap(m -> m.entrySet().stream())
                           .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.summingInt(map -> map.getValue().amount)));

但是我还需要一个Discount对象以及该区域。

我需要一个Discount对象的集合,其数量是相同键的总和,而区域是来自其他票据。

任何帮助将不胜感激。谢谢。

编辑1- 为简单起见,请考虑lastMarketingRegion对于折扣代码具有相同的值。 我还尝试通过图表进行解释-enter image description here

2 个答案:

答案 0 :(得分:3)

来自评论

  

为什么当LUCKY10有“ M2”和“ M5”条目时,为什么会期望“ LUCKY10”-“ M5”?

     

因为其他账单的优先级高于预付款

您可以为此使用Collectors.toMap。最后一个参数是mergeFunction,它合并了两个在地图中具有相同String键的Discounts。

Map<String, Discount> totalDiscounts = discList.stream()
            .flatMap(m -> m.entrySet().stream())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (discount1, discount2) -> new Discount(discount1.getAmount() + discount2.getAmount(),
                            discount2.getLastMarketingRegion())));

由于从列表中生成的流是有序的,因此discount2 折扣将是otherBills映射中的那个,因此,我选择了它的区域

如果您先添加otherBills后再加上prepaid来构建列表,则输出将有所不同。

依靠相遇顺序将其设为非最佳解决方案。 (如果您假设我们在处理完第一个地图后处理了第二个地图中的条目,为什么要首先合并它们?)

查看我使用Map.merge的{​​{3}}

答案 1 :(得分:2)

如果只有两个地图,则可以使用Map.merge来代替基于流的解决方案(我的other answer)。

在这里,我们制作了prepaid地图的副本。然后,我们遍历otherBills映射。对于每个键

  1. 如果该映射不存在,则将其添加到地图(结果地图)
  2. 如果映射已经存在,我们将构造一个新的Discount对象,其数量是地图中已经存在的Discount对象(来自prepaid的Discount对象和当前Discount对象的总和) (来自otherBill的那个)。它从otherBill映射中获取Discount对象的区域。

Map<String, Discount> result = new HashMap<>(prepaid);
otherBills.forEach((k, v) -> result.merge(k, v, (discountFromPrepaid, discountFromOtherBill) ->
        new Discount(discountFromPrepaid.getAmount() + discountFromOtherBill.getAmount(),
                discountFromOtherBill.getLastMarketingRegion())));