Kero
Kero

Reputation: 1944

how to merge two streams and return list with different type?

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

Answers (1)

Holger
Holger

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

Related Questions