繁体   English   中英

使用流比较 Java 中 HashMap 列表的正确方法是什么

[英]What is the right way to compare the List of HashMap in Java using streams

我想比较 Java 中的两个 HashMap 列表。我想了解一种使用 lambda 表达式而不是嵌套 for 循环来比较它们的方法。

两个 HashMap 列表:

  1. 产品详细信息来自输入数据;

样本数据:

    [{ProductName=Prod1, Quantity=1.0, Perpetual=true}, 
    {ProductName=Prod2, Quantity=1.0, Perpetual=false}, 
    {ProductName=Prod3, Quantity=1.0, Perpetual=false}]
  1. productDetailsFromApplication;

样本数据:

    [{Perpetual=true, ProductName=Prod1, Quantity=1.0}, 
    {Perpetual=true,  ProductName=Prod2, Quantity=1.0}, 
    {Perpetual=false, ProductName=Prod3, Quantity=2.0}, 
    {Perpetual=false, ProductName=Prod4, Quantity=1.0}, 
    {Perpetual=false, ProductName=Prod5, Quantity=1.0}, 
    {Perpetual=false, ProductName=Prod6, Quantity=1.0}] 

逻辑:我想遍历 productDetailsFromInputData 列表并获取每个 hashmap 的 ProductName 值,检查该值是否存在于 productDetailsFromApplication 列表中存在的任何哈希映射中。 如果不是,则将结果标记为失败。

如果 ProductName 作为值存在于 productDetailsFromApplication 列表的 hashmap 中的任何一个中,则从该 hashmap 中获取 Perpetual 和 Quantity 值,并将其与 productDetailsFromInputData 列表中的 hashmap 进行比较。

到目前为止,我已经尝试过以下方法:

    for(HashMap<String,Object> inputProductHashMap : productDetailsFromInputData) {
           String productName = inputProductHashMap.get("ProductName").toString();
           String inputQuantity = inputProductHashMap.get("Quantity").toString();
           String inputPerpetual = inputProductHashMap.get("Perpetual").toString();
           LOGGER.info("Validating Product details for Product "+productName);
           if(productDetailsFromApplication.stream().anyMatch(t->t.containsValue(productName))){
               productDetailsFromApplication.stream()
                               .filter(p->p.containsValue(productName))
                               .findAny().ifPresent(p->
                               {
                                   String AppQuantity = p.get("Quantity").toString();
                                   String AppPerpetual = p.get("Perpetual").toString();
                                   LOGGER.info("Found the Product details in SFDC matching with input data");
                                   if(!inputQuantity.equalsIgnoreCase(AppQuantity)){
                                       LOGGER.error("APP Quantity does not matches with Input Quantity for Product "+productName
                                               +" App Quantity "+AppQuantity+" Input Quantity "+inputQuantity);
                                       Assert.fail("Product Validation Failed for "+productName);
                                   }
                                   if(!inputPerpetual.equalsIgnoreCase(AppPerpetual)){
                                       LOGGER.error("App Perpetual value does not matches with Input Perpetual value for Product "
                                               +productName +" App Perpetual "+AppPerpetual+" Input perpetual "+inputPerpetual);
                                       Assert.fail("Product Validation Failed "+productName);
                                   }
                               });

           }
           else {
               LOGGER.error("Did not find any matching Product Name with the given details in App");
               return false;
           }
        }

我想了解是否还有办法删除第一个 for 循环。 由于我对使用 lambda 和流很陌生,我该如何优化它。

此外,我想知道一种方法,如果代码到达LOGGER.error部分,我当前已在其中添加断言以标记失败,则返回 boolean 值。

如果现有的 Stack-overflow 问题涵盖了此问题的解决方案,请随时指导我。

从您的陈述中,我了解到您想返回false而不是运行Assert.fail 仅使用 Streams 的方法是:

return !productDetailsFromInputData.stream().map(i ->
        productDetailsFromApplication.stream().filter((a) -> a.get("ProductName").toString().equalsIgnoreCase(i.get("ProductName").toString())).findAny()
                .map(a ->
                        !a.get("Perpetual").toString().equalsIgnoreCase(i.get("Perpetual").toString()) || !a.get("Quantity").toString().equalsIgnoreCase(i.get("Quantity").toString()))
                .orElse(true)
).anyMatch(b -> b);

请注意,我将失败映射到true ,那是因为anyMatch会寻找任何真值。 然后我们用! 在回报中。

如果你想保持 LOGGER 的冗长,你可以这样做:

return !productDetailsFromInputData.stream().map(i ->
        productDetailsFromApplication.stream().filter((a) -> a.get("ProductName").toString().equalsIgnoreCase(i.get("ProductName").toString())).findAny()
                .map(a -> {
                    if(!a.get("Quantity").toString().equalsIgnoreCase(i.get("Quantity").toString())) {
                        LOGGER.error("APP Quantity does not matches with Input Quantity for Product "+i.get("ProductName").toString()
                                +" App Quantity "+a.get("Quantity").toString()+" Input Quantity "+i.get("Quantity").toString());
                        return true;
                    }
                    if(!a.get("Perpetual").toString().equalsIgnoreCase(i.get("Perpetual").toString())) {
                        LOGGER.error("App Perpetual value does not matches with Input Perpetual value for Product "
                                +i.get("ProductName").toString() +" App Perpetual "+a.get("Perpetual").toString()+" Input perpetual "+i.get("Perpetual").toString());
                        return true;
                    }
                    return false;
                })
                .orElseGet(() -> {
                    LOGGER.error("Did not find any matching Product Name with the given details in App");
                    return true;
                })
).anyMatch(b -> b);

但是,我建议不要只使用 Streams 并首先将productDetailsFromApplication转换为 Map,这样可以更快地找到匹配项:

Map<String, Map<String, Object>> mapFromApplication = productDetailsFromApplication.stream().collect(Collectors.toMap(e -> e.get("ProductName").toString(), Function.identity()));
return !productDetailsFromInputData.stream().map(i ->
        Optional.ofNullable(mapFromApplication.get(i.get("ProductName").toString()))
                .map(a -> {
                    if(!a.get("Quantity").toString().equalsIgnoreCase(i.get("Quantity").toString())) {
                        LOGGER.error("APP Quantity does not matches with Input Quantity for Product "+i.get("ProductName").toString()
                                +" App Quantity "+a.get("Quantity").toString()+" Input Quantity "+i.get("Quantity").toString());
                        return true;
                    }
                    if(!a.get("Perpetual").toString().equalsIgnoreCase(i.get("Perpetual").toString())) {
                        LOGGER.error("App Perpetual value does not matches with Input Perpetual value for Product "
                                +i.get("ProductName").toString() +" App Perpetual "+a.get("Perpetual").toString()+" Input perpetual "+i.get("Perpetual").toString());
                        return true;
                    }
                    return false;
                })
                .orElseGet(() -> {
                    LOGGER.error("Did not find any matching Product Name with the given details in App");
                    return true;
                })
).anyMatch(b -> b);

这将在第一次失败时停止,而不计算所有失败。 如果您可以从计算失败次数和/或记录所有失败次数中获益,则可以使用.filter(b -> b).count()而不是.anyMatch(b -> b)

long count = productDetailsFromInputData.stream().map(i ->
...
.filter(b -> b).count();
if(count>0) {
    LOGGER.error(count+" failures.");
    return false;
} else {
    return true;
}

编辑:您实际上可以将anyMatch语句进一步向上移动以替换第一个map ,但是将它放在底部可以让您轻松地将其更改为其他用途,例如需要时的count

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM