Reputation: 1944
i have two streams and i want to combine them into list with different i.e i have hashmap
Map<String, List<String>> citiesByZip = new HashMap<>();
that hold 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<>();
that hold 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 map each person in list to hashmap of counties to find which county person lives in
List <FinalObject> finalObjects= new ArrayList<>();
finalObjects = Stream.concat(peopleStream.stream(), citiesByZip.entrySet().stream())
.collect(Collectors.toMap(
))
this list should return list of final objects like this
Junior Jane 20000 Alameda
Junior Jane 30000 Alameda
Joseph James 50000 Alameda
.
.
etc
i know that i can do this job in Java 7 with tradition loops but i was wondering if i can do the same thing in java 8 using stream and lambda
Upvotes: 3
Views: 2099
Reputation: 298183
First, you need a data structure for an efficient lookup of a particular zip code, as Map<String, List<String>>
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 may 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 AbstractMap.SimpleEntry
instances, but looks much like the conventional iteration solution. In fact, for the sequential use case, the loop is actually simpler.
Then, you can convert the Person
instances to FinalObject
instances with a single stream operation. Since you didn’t specify the FinalObject
class, I assume
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 the conversion with the zip lookup like
List<FinalObject> finalObjects = persons.stream()
.map(p -> new FinalObject(p.getFirstName(), p.getLastName(),
p.getIncome(), zipToCity.getOrDefault(p.getZipCode(), "Unknown")))
.collect(Collectors.toList());
Though, it might be beneficial 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());
Upvotes: 7