簡體   English   中英

如何在Java8 Streams中通過多個過濾謂詞比較兩個Map列表以識別匹配和不匹配的記錄

[英]How to compare Two List of Map to identify the matching and non matching records with multiple filter predicates in Java8 Streams

要求是使用流使用多個匹配條件從 Map 列表中獲取所有匹配和不匹配的記錄。 即,不是使用單個過濾器來僅比較“電子郵件”,而是需要比較兩個列表以匹配記錄,並使用多個過濾器謂詞來比較 Email 和 Id。

清單 1:

[{"Email","naveen@domain.com", "Id": "A1"}, 
 {"Email":"test@domain.com","id":"A2"}]

列表2:

[{"Email","naveen@domain.com", "Id": "A1"}, 
 {"Email":"test@domain.com","id":"A2"}, 
 {"Email":"test1@domain.com","id":"B1"}]

使用流,我可以在 Email: Matching Records 上使用 Single filter predicate 找到匹配和不匹配的記錄:

[{"Email","naveen@domain.com", "Id": "A1"}, 
 {"Email":"test@domain.com","id":"A2"}]

非匹配記錄:

[{"Email":"test1@domain.com","id":"B1"}]]

有沒有辦法比較電子郵件和 ID 比較而不僅僅是電子郵件

dbRecords.parallelStream().filter(searchData ->
                inputRecords.parallelStream().anyMatch(inputMap ->
                    searchData.get("Email").equals(inputMap.get("Email")))).
                collect(Collectors.toList());

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ListFiltersToGetMatchingRecords {


    public static void main(String[] args) {

        long startTime = System.currentTimeMillis();
        List<Map<String, Object>> dbRecords = createDbRecords();
        List<Map<String, Object>> inputRecords = createInputRecords();

        List<Map<String,Object>> matchinRecords = dbRecords.parallelStream().filter(searchData ->
                inputRecords.parallelStream().anyMatch(inputMap ->
                    searchData.get("Email").equals(inputMap.get("Email")))).
                collect(Collectors.toList());

        List<Map<String,Object>> notMatchinRecords = inputRecords.parallelStream().filter(searchData ->
                dbRecords.parallelStream().noneMatch( inputMap ->
                        searchData.get("Email").equals(inputMap.get("Email"))
                )).collect(Collectors.toList());

        long endTime = System.currentTimeMillis();
        System.out.println("Matching Records: " + matchinRecords.size());
        matchinRecords.forEach(record -> {
            System.out.println(record.get("Email"));
        });

        System.out.println("Non Matching Records" + notMatchinRecords.size());
        notMatchinRecords.forEach(record -> {
            System.out.println(record.get("Email"));
        });
        System.out.println("Non Matching Records" + notMatchinRecords.size());
        System.out.println("Matching Records: " + matchinRecords.size());
        System.out.println("TotalTImeTaken =" + ((endTime-startTime) /1000) + "sec");
    }

    private static List<Map<String, Object>> createDbRecords() {
        List<Map<String, Object>> dbRecords = new ArrayList<>();
        for(int i =0; i< 100; i+=2) {
            Map<String, Object> dbRecord = new HashMap<>();
            dbRecord.put("Email","naveen" + i +"@gmail.com");
            dbRecord.put("Id", "ID" + i);
            dbRecords.add(dbRecord);
        }
        return dbRecords;
    }

    private static List<Map<String, Object>> createInputRecords() {
        List<Map<String, Object>> dbRecords = new ArrayList<>();
        for(int i =0; i< 100; i++) {
            Map<String, Object> dbRecord = new HashMap<>();
            dbRecord.put("Email", "naveen" + i +"@gmail.com");
            dbRecord.put("ID", "ID" + i);
            dbRecords.add(dbRecord);
        }
        return dbRecords;
    }
}

如果您關心性能,則不應將線性搜索與另一個線性搜索結合起來; 當列表變大時,無法通過並行處理修復由此產生的時間復雜度。

您應該首先構建一個允許高效查找的數據結構:

Map<List<?>,Map<String, Object>> inputKeys = inputRecords.stream()
    .collect(Collectors.toMap(
        m -> Arrays.asList(m.get("ID"),m.get("Email")),
        m -> m,
        (a,b) -> { throw new IllegalStateException("duplicate "+a+" and "+b); },
        LinkedHashMap::new));

List<Map<String,Object>> matchinRecords = dbRecords.stream()
    .filter(m -> inputKeys.containsKey(Arrays.asList(m.get("ID"),m.get("Email"))))
    .collect(Collectors.toList());

matchinRecords.forEach(m -> inputKeys.remove(Arrays.asList(m.get("ID"),m.get("Email"))));
List<Map<String,Object>> notMatchinRecords = new ArrayList<>(inputKeys.values());

此解決方案將保留Map的身份。

如果您只對與"Email"鍵關聯的值感興趣,那就簡單多了:

Map<Object,Object> notMatchinRecords = inputRecords.stream()
    .collect(Collectors.toMap(
        m -> m.get("ID"),
        m -> m.get("Email"),
        (a,b) -> { throw new IllegalStateException("duplicate"); },
        LinkedHashMap::new));

Object notPresent = new Object();
Map<Object,Object> matchinRecords = dbRecords.stream()
    .filter(m -> notMatchinRecords.getOrDefault(m.get("ID"), notPresent)
                                  .equals(m.get("Email")))
    .collect(Collectors.toMap(
        m -> m.get("ID"),
        m -> m.get("Email"),
        (a,b) -> { throw new IllegalStateException("duplicate"); },
        LinkedHashMap::new));

notMatchinRecords.keySet().removeAll(matchinRecords.keySet());

System.out.println("Matching Records: " + matchinRecords.size());
matchinRecords.forEach((id,email) -> System.out.println(email));

System.out.println("Non Matching Records" + notMatchinRecords.size());
notMatchinRecords.forEach((id,email) -> System.out.println(email));

第一個變體可以擴展以輕松支持更多/其他地圖條目:

List<String> keys = Arrays.asList("ID", "Email");

Function<Map<String,Object>,List<?>> getKey
    = m -> keys.stream().map(m::get).collect(Collectors.toList());

Map<List<?>,Map<String, Object>> inputKeys = inputRecords.stream()
    .collect(Collectors.toMap(
        getKey,
        m -> m,
        (a,b) -> { throw new IllegalStateException("duplicate "+a+" and "+b); },
        LinkedHashMap::new));

List<Map<String,Object>> matchinRecords = dbRecords.stream()
    .filter(m -> inputKeys.containsKey(getKey.apply(m)))
    .collect(Collectors.toList());

matchinRecords.forEach(m -> inputKeys.remove(getKey.apply(m)));
List<Map<String,Object>> notMatchinRecords = new ArrayList<>(inputKeys.values());

您只需要在比較中添加一個條件

dbRecords.parallelStream().filter(searchData -> 
                  inputRecords.parallelStream().anyMatch(inputMap ->
                                     searchData.get("Email").equals(inputMap.get("Email"))
                                     && searchData.get("id").equals(inputMap.get("id"))))
         .collect(Collectors.toList());

  • noneMatch()添加相同的noneMatch()
  • 使用System.nanoTime()計算平均時間,更准確
  • 嘗試使用和不使用.parallelStream() (只是.stream() )因為不確定它對你有幫助)

這是伙計...

在 Java8 Streams 中比較兩個 Map 列表以識別具有多個過濾謂詞的匹配和非匹配記錄的最有效方法是:

List<Map<String,String>> unMatchedRecords = dbRecords.parallelStream().filter(searchData ->
                inputRecords.parallelStream().noneMatch( inputMap ->
                        searchData.entrySet().stream().noneMatch(value ->
                                inputMap.entrySet().stream().noneMatch(value1 ->
                                        (value1.getKey().equals(value.getKey()) &&
                                                value1.getValue().equals(value.getValue()))))
                )).collect(Collectors.toList());

筆記:

  1. 如果上面使用的 <Map<String,String> 是 <Map<Object,Object>,不要忘記為 .getKey() 和 value.getKey() 應用 .toString()。

  2. 如此獲得的不匹配記錄可以很容易地從列表(即,dbRecords 或 inputRecords)中減去以檢索匹配結果,並且操作很快。

干杯,

舒巴姆·喬漢

為什么不在anyMatch使用&&

anyMatch(inputMap -> searchData.get("Email").equals(inputMap.get("Email")) 
                     && searchData.get("Id").equals(inputMap.get("Id")))

而且我懷疑您是否真的需要parallelStream ,另一方面您確實需要System.nanoTime而不是currentTimeMillis

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM