So, I have two collections, and I wish to combine both lists somehow.
Requirements are thusly:
- I have a collection newPersons
- I have a collection oldPersons
- I want a collection containing all the oldPersons but replaced (*some of) the oldPersons with the newPersons (based on id).
public record Person(Long id, String name, String surname) {
}
Using the record class above.
Solution 1.
Create a new list based on oldPersons, replace items in this new list with equivalent items in newPersons.
This solution leaves much to be desired. It's confusing and error prone.
I was looking for something better.
public List<Person> mergeMetadata(List<Person> newPersons,
List<Person> oldPersons) {
var result = new ArrayList<>(oldPersons);
newPersons.forEach(newPerson -> {
result.stream()
.filter(person -> person.id().equals(newPerson.id()))
.findFirst()
.ifPresent(result::remove);
result.add(newPerson);
});
return result;
}
Solution 2.
Sometimes getting back to our roots using for-loops can help readability.
We could try it the other way around, see if that helps.
This time we create a new list based on newPersons and add an oldPerson if it's not already in the list.
This seems a little more clear.
public List<Person> mergeMetadata(List<Person> newPersons,
List<Person> oldPersons) {
var result = new ArrayList<>(newPersons);
for (Person oldPerson : oldPersons) {
if (result.stream().noneMatch(x -> x.id().equals(oldPerson.id()))) {
result.add(oldPerson);
}
}
return result;
}
Solution 3.
Merge two collections into one list, by using a map.
public List<Person> mergeMetadata(List<Person> newPersons,
List<Person> oldPersons) {
Map<Long, Person> result = Stream
.concat(newPersons.stream(), oldPersons.stream())
.collect(Collectors.toMap(Person::id, Function.identity(), (l, r) -> l));
return new ArrayList<>(result.values());
}
Although this solution seems to be the shortest (in code), using a Map function can be a bit daunting (it was for me) because of inherent complexity in the method call for creating it.
Still, perhaps it's just me and my inexperience with combining maps and streams.
I don't know if there's an even better way. Will keep an eye out.