如何在 Java8 Streams 中比较两个 Map 列表以识别具有多个过滤谓词的匹配和不匹配记录

Nav*_*D C 3 java java-8

要求是使用流使用多个匹配标准从映射列表中获取所有匹配和不匹配的记录。即,不需要使用单个过滤器来仅比较“电子邮件”,而是需要使用多个过滤器谓词来比较两个列表以匹配记录,以比较电子邮件和 Id 两者。

清单1:

[{"Email","naveen@domain.com", "Id": "A1"}, 
 {"Email":"test@domain.com","id":"A2"}]
Run Code Online (Sandbox Code Playgroud)

清单2:

[{"Email","naveen@domain.com", "Id": "A1"}, 
 {"Email":"test@domain.com","id":"A2"}, 
 {"Email":"test1@domain.com","id":"B1"}]
Run Code Online (Sandbox Code Playgroud)

使用流,我可以使用电子邮件上的单个过滤器谓词找到匹配和不匹配的记录:匹配记录:

[{"Email","naveen@domain.com", "Id": "A1"}, 
 {"Email":"test@domain.com","id":"A2"}]
Run Code Online (Sandbox Code Playgroud)

不匹配的记录:

[{"Email":"test1@domain.com","id":"B1"}]]
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以同时比较电子邮件和 ID,而不仅仅是电子邮件

dbRecords.parallelStream().filter(searchData ->
                inputRecords.parallelStream().anyMatch(inputMap ->
                    searchData.get("Email").equals(inputMap.get("Email")))).
                collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 5

如果您关心性能,则不应将线性搜索与另一个线性搜索结合起来;当列表变大时,所产生的时间复杂度不能通过并行处理来修复。

\n\n

您应该首先构建一个允许高效查找的数据结构:

\n\n
Map<List<?>,Map<String, Object>> inputKeys = inputRecords.stream()\n    .collect(Collectors.toMap(\n        m -> Arrays.asList(m.get("ID"),m.get("Email")),\n        m -> m,\n        (a,b) -> { throw new IllegalStateException("duplicate "+a+" and "+b); },\n        LinkedHashMap::new));\n\nList<Map<String,Object>> matchinRecords = dbRecords.stream()\n    .filter(m -> inputKeys.containsKey(Arrays.asList(m.get("ID"),m.get("Email"))))\n    .collect(Collectors.toList());\n\nmatchinRecords.forEach(m -> inputKeys.remove(Arrays.asList(m.get("ID"),m.get("Email"))));\nList<Map<String,Object>> notMatchinRecords = new ArrayList<>(inputKeys.values());\n
Run Code Online (Sandbox Code Playgroud)\n\n

该解决方案将保留 s 的身份Map

\n\n

如果您只对与键关联的值感兴趣"Email",那么会简单得多:

\n\n
Map<Object,Object> notMatchinRecords = inputRecords.stream()\n    .collect(Collectors.toMap(\n        m -> m.get("ID"),\n        m -> m.get("Email"),\n        (a,b) -> { throw new IllegalStateException("duplicate"); },\n        LinkedHashMap::new));\n\nObject notPresent = new Object();\nMap<Object,Object> matchinRecords = dbRecords.stream()\n    .filter(m -> notMatchinRecords.getOrDefault(m.get("ID"), notPresent)\n                                  .equals(m.get("Email")))\n    .collect(Collectors.toMap(\n        m -> m.get("ID"),\n        m -> m.get("Email"),\n        (a,b) -> { throw new IllegalStateException("duplicate"); },\n        LinkedHashMap::new));\n\nnotMatchinRecords.keySet().removeAll(matchinRecords.keySet());\n\nSystem.out.println("Matching Records: " + matchinRecords.size());\nmatchinRecords.forEach((id,email) -> System.out.println(email));\n\nSystem.out.println("Non Matching Records" + notMatchinRecords.size());\nnotMatchinRecords.forEach((id,email) -> System.out.println(email));\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

第一个变体可以轻松扩展以支持更多/其他地图条目:

\n\n
List<String> keys = Arrays.asList("ID", "Email");\n\nFunction<Map<String,Object>,List<?>> getKey\n    = m -> keys.stream().map(m::get).collect(Collectors.toList());\n\nMap<List<?>,Map<String, Object>> inputKeys = inputRecords.stream()\n    .collect(Collectors.toMap(\n        getKey,\n        m -> m,\n        (a,b) -> { throw new IllegalStateException("duplicate "+a+" and "+b); },\n        LinkedHashMap::new));\n\nList<Map<String,Object>> matchinRecords = dbRecords.stream()\n    .filter(m -> inputKeys.containsKey(getKey.apply(m)))\n    .collect(Collectors.toList());\n\nmatchinRecords.forEach(m -> inputKeys.remove(getKey.apply(m)));\nList<Map<String,Object>> notMatchinRecords = new ArrayList<>(inputKeys.values());\n
Run Code Online (Sandbox Code Playgroud)\n