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

时间:2018-07-11 07:55:38

标签: java java-8

要求是使用流使用多个匹配条件从“地图列表”中获取所有匹配和不匹配的记录。即,不是使用单个过滤器来只比较“电子邮件”,而是需要比较两个列表以匹配记录,并比较多个过滤谓词来比较电子邮件和ID。

列表1:

fuzzy(sample[3], sample[4])

列表2:

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

使用流,我可以使用Email上的Single filter谓词查找匹配和不匹配的记录: 匹配记录:

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

不匹配的记录:

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

是否可以同时比较电子邮件和ID比较,而不只是电子邮件

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

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

4 个答案:

答案 0 :(得分:3)

如果您关心性能,则不应将线性搜索与另一个线性搜索结合使用;当列表变大时,使用并行处理无法解决由此带来的时间复杂性。

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

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());

答案 1 :(得分:0)

为什么不在&&内使用anyMatch

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

我怀疑您实际上是否需要parallelStream,另一方面您确实需要System.nanoTime而不是currentTimeMillis

答案 2 :(得分:0)

您只需要在比较中添加一个条件

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()中添加相同的内容。
  • 使用System.nanoTime()计算平均时间,这样更准确
  • 尝试使用和不使用.parallelStream()(只是.stream()),因为不确定它是否可以帮助您

答案 3 :(得分:0)

这是伴侣...

比较两个Map列表以使用Java8 Streams中的多个过滤谓词来识别匹配记录和不匹配记录的最有效方法是:

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. 如果上面使用的改为,请不要忘记为.getKey()和value.getKey()应用.toString()。

  2. 这样获得的不匹配记录可以很容易地从列表中的任何一个(即dbRecords或inputRecords)中减去,以检索匹配结果,并且操作迅速。

干杯

Shubham Chauhan