【问题标题】:What is the right way to compare the List of HashMap in Java using streams使用流比较Java中的HashMap列表的正确方法是什么
【发布时间】:2021-11-26 00:01:02
【问题描述】:

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

HashMap 的两个列表:

  1. productDetailsFromInputData;

样本数据:

    [{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 列表并获取每个哈希图的 ProductName 值,检查该值是否存在于 productDetailsFromApplication 列表中存在的任何哈希图中。如果不是,则将结果标记为失败。

如果 ProductName 作为值存在于 productDetailsFromApplication List 的任何一个 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 部分,我还想知道一种返回布尔值的方法,我目前在该部分添加了断言来标记失败。

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

【问题讨论】:

  • Lambda 还是流?它是哪一个?它们不是一回事。
  • 为什么?比较它们的正确方法是使用List.equals()。您不必执行任何这些操作。

标签: java lambda java-stream


【解决方案1】:

从您的陈述中,我了解到您想要返回 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 -&gt; b).count() 而不是.anyMatch(b -&gt; 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 2013-04-25
    相关资源
    最近更新 更多