[英]Get List<String> and HashMap<String, Object> using Java 8 streams
[英]What is the right way to compare the List of HashMap in Java using streams
我想比較 Java 中的兩個 HashMap 列表。我想了解一種使用 lambda 表達式而不是嵌套 for 循環來比較它們的方法。
兩個 HashMap 列表:
樣本數據:
[{ProductName=Prod1, Quantity=1.0, Perpetual=true},
{ProductName=Prod2, Quantity=1.0, Perpetual=false},
{ProductName=Prod3, Quantity=1.0, Perpetual=false}]
樣本數據:
[{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.