Reputation: 71
Requirement is to get all the matching and non matching records from the List of Map using multiple matching criteria using the streams. i.e Instead of having a single filter to compare only "Email", required to compare two list for matching records with multiple filter predicate for comparing Email and Id both.
List1:
[{"Email","[email protected]", "Id": "A1"},
{"Email":"[email protected]","id":"A2"}]
List2:
[{"Email","[email protected]", "Id": "A1"},
{"Email":"[email protected]","id":"A2"},
{"Email":"[email protected]","id":"B1"}]
Using streams I'm able to find the matching and non matching records using Single filter predicate on Email: Matching Records :
[{"Email","[email protected]", "Id": "A1"},
{"Email":"[email protected]","id":"A2"}]
Non Matching Records :
[{"Email":"[email protected]","id":"B1"}]]
Is there a way to compare both Email and Id comparison instead of just Email
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;
}
}
Upvotes: 3
Views: 12900
Reputation: 11
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());
Note:
If <Map<String,String> used above is <Map<Object,Object> instead, don't forget to apply .toString() for .getKey() and value.getKey().
The unmatched records thus obtained, could be easily subtracted from either of the list (i.e., dbRecords or inputRecords) to retrieve the match results and the operation is swift.
Cheers,
Shubham Chauhan
Upvotes: 1
Reputation: 298399
If you care for performance, you should not combine a linear search with another linear search; with the resulting time complexity can’t be fixed with parallel processing when the lists get large.
You should built a data structure which allows efficient lookups first:
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());
This solution will keep the identity of the Map
s.
If you are only interested in the values associated with the "Email"
key, it would be much simpler:
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));
The first variant can get extended to support more/other map entries easily:
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());
Upvotes: 5
Reputation: 54168
You just need to add a condition in the comparison
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()
, it's more accurate.parallelStream()
(just .stream()
) because not sure it helps you)Upvotes: 2
Reputation: 120968
Why not use &&
inside anyMatch
:
anyMatch(inputMap -> searchData.get("Email").equals(inputMap.get("Email"))
&& searchData.get("Id").equals(inputMap.get("Id")))
And I doubt you actually need parallelStream
, you do need System.nanoTime
on the other hand instead of currentTimeMillis
Upvotes: 0