How can I concatenate two streams and return a list with a different type?
I have two streams and I want to combine them into a list with different ie I have a hashmap
Map<String, List<String>> citiesByZip = new HashMap<>();
which store this data
Alameda [95246, 95247]
Colusa [95987]
list of persons
class Person {
private String firstName;
private String lastName;
private int income;
private int zipCode;
People(String firstName, String lastName, int income, int zipCode) {
this.firstName = firstName;
this.lastName = lastName;
this.income = income;
this.zipCode = zipCode;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getIncome() {
return income;
}
public int getZipCode() {
return zipCode;
}
}
List<Person> persons= new ArrayList<>();
which store this data
Junior Jane 20000 95246
Junior Jane 30000 95246
Joseph James 50000 95247
Patricia Allen 60000 95247
Opal Campbell 70000 95987
Dorothy Rook 80004 95987
Mary Nelson 80000 23666
I want to match each person on the list in the counties hashmap to find which count person lives in
List <FinalObject> finalObjects= new ArrayList<>(); finalObjects = Stream.concat(peopleStream.stream(), citiesByZip.entrySet().stream()) .collect(Collectors.toMap( ))
this list should return a list of target objects like this
Junior Jane 20000 Alameda
Junior Jane 30000 Alameda
Joseph James 50000 Alameda
.
.
etc
I know I can make this work in Java 7 with tradition loops, but I was wondering if I could do the same thing in java 8 using stream and lambda
source to share
First, you need a data structure to efficiently find a specific zip code, as Map<String, List<String>>
it is not suitable for that. You can convert it like
Map<Integer,String> zipToCity = citiesByZip.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(Integer::valueOf)
.map(zip -> new AbstractMap.SimpleEntry<>(zip, e.getKey())))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Alternatively, you can use
Map<Integer,String> zipToCity = citiesByZip.entrySet().stream()
.collect(HashMap::new,
(m,e) -> e.getValue().forEach(zip -> m.put(Integer.valueOf(zip), e.getKey())),
Map::putAll);
which doesn't need temporary instances AbstractMap.SimpleEntry
, but looks like a normal iterative solution. In fact, for consistent use, the loop is actually simpler.
Then you can convert instances Person
to instances FinalObject
using a single thread operation. Since you did not specify the class FinalObject
, I am assuming
class FinalObject {
private String firstName, lastName, city;
private int income;
FinalObject(String firstName, String lastName, int income, String city) {
this.firstName = firstName;
this.lastName = lastName;
this.income = income;
this.city = city;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getIncome() {
return income;
}
public String getCity() {
return city;
}
@Override public String toString() {
return firstName+" "+lastName+" "+income+" "+city;
}
}
With this definition, you can do a zip search conversion like
List<FinalObject> finalObjects = persons.stream()
.map(p -> new FinalObject(p.getFirstName(), p.getLastName(),
p.getIncome(), zipToCity.getOrDefault(p.getZipCode(), "Unknown")))
.collect(Collectors.toList());
Although it might be helpful to use delegation instead:
class FinalObject {
private Person p;
String city;
FinalObject(Person p, String city) {
this.p = p;
this.city = city;
}
public String getFirstName() {
return p.getFirstName();
}
public String getLastName() {
return p.getLastName();
}
public int getIncome() {
return p.getIncome();
}
public String getCity() {
return city;
}
@Override public String toString() {
return getFirstName()+" "+getLastName()+" "+getIncome()+" "+city;
}
}
List<FinalObject> finalObjects = persons.stream()
.map(p -> new FinalObject(p, zipToCity.getOrDefault(p.getZipCode(), "Unknown")))
.collect(Collectors.toList());
source to share