Find element matching in 2 lists using java 8 stream
Define yourself a key object that holds and compares the desired properties. In this simple case, you may use a small list whereas each index corresponds to one property. For more complex cases, you may use a Map
(using property names as keys) or a dedicated class:
Function<Person,List<Object>> toKey=p -> Arrays.asList(p.getName(), p.getAge());
Having such a mapping function. you may use the simple solution:
list1.stream().map(toKey)
.flatMap(key -> list2.stream().map(toKey).filter(key::equals))
.forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));
which may lead to poor performance when you have rather large lists. When you have large lists (or can’t predict their sizes), you should use an intermediate Set
to accelerate the lookup (changing the task’s time complexity from O(n²)
to O(n)
):
list2.stream().map(toKey)
.filter(list1.stream().map(toKey).collect(Collectors.toSet())::contains)
.forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));
In the examples above, each match gets printed. If you are only interested in whether such a match exists, you may use either:
boolean exists=list1.stream().map(toKey)
.anyMatch(key -> list2.stream().map(toKey).anyMatch(key::equals));
or
boolean exists=list2.stream().map(toKey)
.anyMatch(list1.stream().map(toKey).collect(Collectors.toSet())::contains);
Java - How can I merge 2 lists based on a third?
I think this solution will work - it preserves the order of the lists, and gave the correct answers to all the samples shown above. By keeping the reference list as a hashMap of indexes, it allows to quickly query for any two integers if one can appear before the other by the reference. I added a printout in a main to test.
public class ListMergeByRef {
public Map<Integer, List<Integer>> refMap = new HashMap<Integer,List<Integer>>();
public ListMergeByRef(List<Integer> reference) {
int elementIndex = 0;
for (Integer element:reference) {
List<Integer> refListPerElement = refMap.get(element);
if (refListPerElement == null) {
refListPerElement = new ArrayList<Integer>();
}
refListPerElement.add(elementIndex);
elementIndex++;
refMap.put(element, refListPerElement);
}
}
public List<Integer> mergeLists (List<Integer> first, List<Integer> second) {
int firstIndex = 0;
int secondIndex = 0;
List<Integer> merged = new ArrayList<Integer>();
while (firstIndex < first.size() || secondIndex < second.size()) {
if (firstIndex == first.size()) {
merged.addAll(second.subList(secondIndex, second.size()));
return merged;
} else if (secondIndex == second.size()) {
merged.addAll(first.subList(firstIndex, first.size()));
return merged;
}
if (first.get(firstIndex).equals(second.get(secondIndex))){
merged.add(first.get(firstIndex));
firstIndex++;
secondIndex++;
}
else if (isElementAllowedBeforeOther(first.get(firstIndex), second.get(secondIndex))) {
merged.add(first.get(firstIndex));
firstIndex++;
} else {
merged.add(second.get(secondIndex));
secondIndex++;
}
}
return merged;
}
public boolean isElementAllowedBeforeOther(Integer firstElement, Integer secondElement) {
List<Integer> firstElementIndexes = refMap.get(firstElement);
List<Integer> secondElementIndexes = refMap.get(secondElement);
if (firstElementIndexes == null || firstElementIndexes.isEmpty()) return false;
if (secondElementIndexes == null || secondElementIndexes.isEmpty()) return true;
if (firstElementIndexes.get(0) < secondElementIndexes.get(secondElementIndexes.size()-1)) return true;
return false;
}
public static void main(String[] args) {
List<Integer> ref = Arrays.asList(new Integer[] {1,2,3,4,5,6,4,7,8});
List<Integer> first = Arrays.asList(new Integer[] {2,3,4,5,7});
List<Integer> second = Arrays.asList(new Integer[] {4,7,8});
ListMergeByRef merger = new ListMergeByRef(ref);
List<Integer> mergedList = merger.mergeLists(first, second);
for (Integer element: mergedList) {
System.out.print(element+" ");
}
System.out.println();
}
Merging objects from two list with unique id using Java 8
Based on your updated question:
List<Human> humans = ... // init
List<SuperHuman> superHumans = ... // init
Set<Long> superHumanIds = superHumans.stream()
.map(SuperHuman::getHumanId)
.collect(toSet());
humans.stream()
.filter(human -> superHumanIds.contains(human.getHumanId()))
.map(this::convert)
.forEach(superHumans::add);
superHumans.sort(Comparator.comparing(SuperHuman::getAge));
Assuming this class has another method with the following signature:
private Superhuman convert(Human human) {
// mapping logic
}
Related Topics
How to Get the Autoincremented Id When I Insert a Record in a Table Via Jdbctemplate
How to Convert Date Which I Got from Firebase Server as Am Getting Error Date
How to Put a Scanner Input into an Array... for Example a Couple of Numbers
Getting Column Names from a JPA Native Query
How to Test for Blank Line With Java Scanner
@Autowired - No Qualifying Bean of Type Found for Dependency
Spring Boot Rest API Returns 404
Jpa: Update Only Specific Fields
Spring Boot: Cannot Access Rest Controller on Localhost (404)
How to Configure Hikaricp in My Spring Boot App in My Application.Properties Files
How to Enable Request Scope in Async Task Executor
How to Load 100 Million Rows in to Memory
Error: Incompatible Types: List<Integer> Cannot Be Converted to Arraylist<Integer>
Extract Text Br Tags in Selenium Java
Eclipse: the Resource Is Not on the Build Path of a Java Project
Why Does Intellij Idea Suddenly Not Recognize Tests in Test Folder Anymore