Well, I don't like nested loops very much ... Is it mandatory to create a one-liner stream operation? What about splitting the code a bit to improve readability?
Let's assume that streetNumber is a key assigned to a pair streetName/zipCode and that you're trying to copy that information from AddressInfo to EmpInfo using such field as "matching column".
I've programmed a little functional example using these classes
EmpInfo
public class EmpInfo {
private String id;
private Long streetNumber;
private String streetName;
private String streetZipCode;
/** get/set methods */
@Override
public String toString() {
return "EmpInfo [id=" + id + ", streetNumber=" + streetNumber
+ ", streetName=" + streetName + ", streetZipCode=" + streetZipCode + "]";
}
}
AddressInfo
public class AddressInfo {
private String id;
private Long streetNumber;
private String streetName;
private String streetZipCode;
/** get/set methods */
}
TestAddress
public class TestAddress {
public static void main(String[] args) {
List<EmpInfo> listEmp = new ArrayList<>();
EmpInfo emp1 = new EmpInfo("Fail1", null, null, null);
EmpInfo emp2 = new EmpInfo("Match1", Long.valueOf(1), null, null);
EmpInfo emp3 = new EmpInfo("Match2", Long.valueOf(1), "streeNameEmp", "zipCodeEmp");
EmpInfo emp4 = new EmpInfo("Match3", Long.valueOf(2), "streeNameEmp", "zipCodeEmp");
EmpInfo emp5 = new EmpInfo("Fail3", Long.valueOf(3), "streeNameEmp", "zipCodeEmp");
listEmp.add(emp1);
listEmp.add(emp2);
listEmp.add(emp3);
listEmp.add(emp4);
listEmp.add(emp5);
List<AddressInfo> listAddress = new ArrayList<>();
AddressInfo add1 = new AddressInfo("Address1", Long.valueOf(1), "Street One", "Zip One");
AddressInfo add2 = new AddressInfo("Address2", Long.valueOf(2), "Street Two", "Zip Two");
AddressInfo add4 = new AddressInfo("Address4", Long.valueOf(4), "Street Three", "Zip Four");
AddressInfo add5 = new AddressInfo("Address5", null, "Street Three", "Zip Four");
listAddress.add(add1);
listAddress.add(add2);
listAddress.add(add4);
listAddress.add(add5);
Map<Long, AddressInfo> mapAddresses =
listAddress.stream().filter(address -> address.getStreetNumber() != null)
.collect(Collectors.toMap(AddressInfo::getStreetNumber,
Function.identity()));
listEmp.stream().filter(emp -> mapAddresses.containsKey(emp.getStreetNumber()))
.forEach(emp -> copyAddressData2Emp(mapAddresses, emp));
listEmp.stream().forEach(emp -> System.out.println(emp));
}
private static void copyAddressData2Emp(Map<Long, AddressInfo> mapAddresses, EmpInfo emp) {
AddressInfo addressInfo = mapAddresses.get(emp.getStreetNumber());
emp.setStreetName(addressInfo.getStreetName());
emp.setStreetZipCode(addressInfo.getStreetZipCode());
}
}
What I have done? First I've created a Map with the AddressInfo objects using field streetNumber as key. Those AddressInfo whose streetNumber is null have been previously filtered.
Map<Long, AddressInfo> mapAddresses =
listAddress.stream().filter(address -> address.getStreetNumber() != null)
.collect(Collectors.toMap(AddressInfo::getStreetNumber,
Function.identity()));
Then after filtering those EmpInfo who match an AddressInfo, the information is copied from the matching AddressInfo object to the EmpInfo object. Notice that EmpInfo with null values in streetNumber are filtered from the stream without without explicitly forcing it.
listEmp.stream().filter(emp -> mapAddresses.containsKey(emp.getStreetNumber()))
.forEach(emp -> copyAddressData2Emp(mapAddresses, emp));
Probably you can simplify this stream operation, some way of passing the matching AddressInfo to the following operation in the stream, but I've not found a simpler option. Suggestions are welcome.