Reputation: 2461
suppose I have these two lists
List<Person> persons = Arrays.asList(
new Person(1, "Mike", "Canada"),
new Person(2, "Jill", "England"),
new Person(3, "Will", "Whales"),
new Person(4, "Mary", "Spain"));
List<Metadata> metadata= Arrays.asList(
new metadata(1, "2000-01-01", "Naturalized", "Bachelor's of Arts"),
new metadata(2, "2001-01-01", "ExPat", "Masters of Chemestry"),
new metadata(3, "2017-05-01", "Citizen", "Buiness Management"),
new metadata(4, "2018-04-16", "Work Visa", "Nursing"));
where the end result is a new list:
List<PersonWithMetadata> personsAndMEtadata = Arrays.asList(
new PersonWithMetadata(1, "Mike", "Canada", "2000-01-01", "Naturalized", "Bachelor's of Arts"),
new PersonWithMetadata(2, "Jill", "England", "2001-01-01", "ExPat", "Masters of Chemestry"),
new PersonWithMetadata(3, "Will", "Whales", "2017-05-01", "Citizen", "Buiness Management"),
new PersonWithMetadata(4, "Mary", "Spain", "2018-04-16", "Work Visa", "Nursing"));
I am trying to find a Java streams way of combining the first two lists into the third--like an SQL join on the first input being an id number. It seems like there should be a way to do this, but I'm at a loss. how is this done? Also, suppose that there is at most one match between the two input lists.
Upvotes: 3
Views: 1572
Reputation: 311163
YCF_L's solution should work, but it's an O(n2) solution. An O(n) solution could be achieved by converting one list to map from the id to the object, and then iterating over the other and getting the matching value from the map:
Map<Integer, Person> personMap =
persons.stream().collect(Collectors.toMap(Person::getId, Function.identity());
List<PersonWithMetadata> result =
metadata.stream()
.map(m -> new PersonWithMetadata(personMap.get(m.getId()), m)
.collect(Collectors.toList());
In the sample data the lists have matching objects in matching order. If this assumption is true for the real problem too, the solution could be must easier - you could stream the indexes and get the corresponding values from the lists:
List<PersonWithMetadata> result =
IntStream.reange(0, persons.size())
.map(i -> new PersonWithMetadata(persons.get(i), metadata.get(i))
.collect(Collectors.toList());
Upvotes: 5
Reputation: 1005
The below example builds Map
of Metadata
objects using the ID as the key. This will help with performance as there's no need to iterate over the Metadata
list for each Person
in the List
Code
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person(1, "Mike", "Canada"),
new Person(2, "Jill", "England"),
new Person(3, "Will", "Whales"),
new Person(4, "Mary", "Spain"));
List<Metadata> metadataList = Arrays.asList(
new Metadata(1, "2000-01-01", "Naturalized", "Bachelor's of Arts"),
new Metadata(2, "2001-01-01", "ExPat", "Masters of Chemestry"),
new Metadata(3, "2017-05-01", "Citizen", "Buiness Management"),
new Metadata(4, "2018-04-16", "Work Visa", "Nursing"));
//Iterate over metadataList once and create map based on ID as key
Map<Integer, List<Metadata>> metadataMap = metadataList.stream()
.collect(Collectors.groupingBy(Metadata::getId));
//Iterate over personList and fetch metadata from Map to build PersonWithMetadata
List<PersonWithMetadata> personWithMetadataList = persons.stream().map(person -> {
List<Metadata> metadata = metadataMap.get(person.id);
if (metadata.isEmpty()) {
//TODO: Handle scenario for no metadata for person
}
//TODO: Build PersonWithMetadata
return new PersonWithMetadata();
}).collect(Collectors.toList());
}
Upvotes: 1
Reputation: 7553
I believe what you're looking for is the zip
function that was sadly omitted from the API.
The protonpack library provides it, which would allow you to zip and then map the tuple to the new structure.
StreamUtils.zip(persons, metadata, (person, metadata) -> ... )
Upvotes: 1
Reputation: 59960
You can try this way :
List<PersonWithMetadata> personsAndMEtadata = persons.stream()
.map(p -> {
//search for the meta data based on the person id
Metadata meta = metadata.stream()
.filter(m -> m.getId() == p.getId())
.findFirst()
.get();
// then create a PersonWithMetadata object based on Person and metadata
return new PersonWithMetadata(
p.getId(), p.getFirstName(), p.getLastName(),
meta.getDate(), meta.getCity(), meta.getJob()
);
}
).collect(Collectors.toList());
About this line :
Metadata meta = metadata.stream().filter(m -> m.getId() == p.getId()).findFirst().get();
I assume that you have a meta data with the id of person, else you will get NullPointerException
.
Upvotes: 3