簡體   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