Is there a way to process each class attribute in parallel using java 8 lambda?
For example, for a given Car.java:
public class Car{
private String color;
private String model;
private float value;
private float tax;
// and others attributes
.
.
.
// Getters and Setters
}
I would like to make some process over color, model, value, tax, etc in parallel. So basically the wished logic would be:
parallel processing:
- color
- model
- value
- tax
then join and update Car.java object instance fields.
Please, note that this problem is independent of the number of cars instances. It could be just one or many.
I was curious, how that would look like. Assuming the restriction holds, that the processing of each of the properties is independent of one another, that would be one way to utilize parallelStream(). But I doubt very much, that this pays off due to the overhead of the parallel machinery. One could make it even more outlandish using reactive streams, e.g. RxJava.
public class Car{
public String color;
public String model;
public float value;
public float tax;
public Car(String color, String model, float value, float tax) {
this.color = color;
this.model = model;
this.value = value;
this.tax = tax;
}
#Override
public String toString() {
return "Car{" +
"color='" + color + '\'' +
", model='" + model + '\'' +
", value=" + value +
", tax=" + tax +
'}';
}
}
#Test
public void process() {
List<Consumer<Car>> processors = Arrays.asList(
c -> c.color = printThread(c.color.toLowerCase()),
c -> c.model = printThread(c.model.toLowerCase()),
c -> c.value = printThread(c.value * c.value),
c -> c.tax = printThread(c.tax / c.tax));
Arrays.asList(new Car("Red", "AlphaGorilla", 1f, 0.5f), new Car("Blue", "Bloated++", 10f, 0.2f))
.parallelStream().forEach(c -> {
System.out.println(c);
processors.parallelStream().forEach(p -> {
p.accept(c);
fakeExpensiveComputation();
});
System.out.println(c);
});
}
private <T> T printThread(T smthg) {
System.out.println(String.format("Calculated value %s in thread %d", smthg.toString(), Thread.currentThread().getId()));
return smthg;
}
private void fakeExpensiveComputation() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException();
}
}
Related
I'm stuck with one of the final drills in my java programming course and would appreciate a pointer as to what I should be doing to satisfy the outcomes required.
The code will be tested by the following code which can't be altered:
Test:
int wheels = 4;
String make = "Honda";
String color = "Yellow";
Vehicle v1 = new Vehicle(make);
System.out.println(v1.aboutMe());
Vehicle v2 = new Vehicle(make, color);
System.out.println(v2.aboutMe());
Vehicle v3 = new Vehicle(make, color, wheels);
System.out.println(v3.aboutMe());
Result:
Vehicle object created.
Make: Honda
Colour: Blue
Wheels: 4
Vehicle object created.
Make: Honda
Colour: Yellow
Wheels: 4
Vehicle object created.
Make: Honda
Colour: Yellow
Wheels: 4
The code I have written is here but doesn't compile (temporary solution is greyed out but doesn't meet testing criteria as alters main method to passes parameters from the instance):
public class Vehicle {
// TODO your code goes here
// declare instance fields here!
String make;
String color;
int wheels;
public String aboutMe() {
String statement = "Make: " + make + "\n" + "Colour: " + color + "\n" + "Wheels: " + wheels;
return statement;
}
// constructor method
public Vehicle(String vehicleMake, String vehicleColor, int vehicleWheels) {
make = vehicleMake;
color = vehicleColor;
wheels = vehicleWheels;
}
//Test:
public static void main(String[] args) {
int wheels = 4;
String make = "Honda";
String color = "Yellow";
System.out.println("Vehicle object created.");
//CODE REQUIRED BY TEST:
Vehicle v1 = new Vehicle(make);
//Vehicle vh1 = new Vehicle("Honda", "Blue", 4);
System.out.println(vh1.aboutMe());
//CODE REQUIRED BY TEST:
Vehicle v2 = new Vehicle(make, color);
//Vehicle vh2 = new Vehicle("Honda", "Yellow", 4);
System.out.println(vh2.aboutMe());
//CODE REQUIRED BY TEST:
Vehicle v3 = new Vehicle(make, color, wheels);
//Vehicle vh3 = new Vehicle("Honda", "Yellow", 4);
System.out.println(vh3.aboutMe());
}
//Code stops here
}
In summary, how do I pass the instances declared in the main to the constructor?
Thanks kindly, this is my first post on here so much appreciated for bearing with me while I learn the correct protocol for asking questions on here.
Edit: thanks kindly the code runs now much appreciated
public class Vehicle {
// TODO your code goes here
// declare instance fields here!
String make;
String color;
int wheels;
public String aboutMe() {
String statement = "Vehicle object created.\n" + " Make: " + make + "\n" + " Colour: " + color + "\n" + " Wheels: " + wheels;
return statement;
}
//constructor method
public Vehicle(String vehicleMake, String vehicleColor, int vehicleWheels) {
make = vehicleMake;
color = vehicleColor;
wheels = vehicleWheels;
}
public Vehicle(String make) {
this(make, "Blue");
}
public Vehicle(String make, String color) {
this(make, color, 4);
}
public static void main(String[] args) {
int wheels = 4;
String make = "Honda";
String color = "Yellow";
//System.out.println("Vehicle object created.");
Vehicle v1 = new Vehicle(make);
System.out.println(v1.aboutMe());
Vehicle v2 = new Vehicle(make, color);
System.out.println(v2.aboutMe());
Vehicle v3 = new Vehicle(make, color, wheels);
System.out.println(v3.aboutMe());
}
// Code stops here
}
You need to create other constructors
public Vehicle(String make) {
this(make, "Blue");
}
public Vehicle(String make, String color) {
this(make, color, 4);
}
I can't figure it out what getExits() needs to be in order to get the output requested by the problem.
//Constructor
public class Room {
private String name;
private String description;
private Room north;
private Room east;
private Room south;
private Room west;
public Room (String name, String description){
this.name = name;
this.description = description;
}
public Room getEast(){
return this.east;
}
public String getExits (){
//
}
public String getName(){
return this.name;
}
public Room getNorth(){
return this.north;
}
public Room getWest(){
return this.west;
}
public Room getSouth(){
return this.south;
}
public void setExits (Room n, Room e, Room w, Room s){
this.north = n;
this.east = e;
this.west = w;
this.south = s;
}
public String toString(){
return String.format("%s\n%s\n%s", this.name, this.description,getExits());
}
}
//Main Method
public class Tester{
public static void main(String []args){
Room hall = new Room ("Hall", "It's Dark");
Room bed = new Room ("Bed", "Tiny Room");
Room bath = new Room ("Bath", "Toilets here");
Room dine = new Room ("Dine", "Table and chairs");
hall.setExits(bed, bath, dine, null);
System.out.println(hall);
}
}
Output expected:
Hall
It's Dark
North: Dine
East: Bath
West: Dining
The 'Object Oriented' way of getting what you want would be to override the toString() method in your Room class such that it returns the name of the room.
You then modify the getExits(), like so:
public String getExits (){
StringBuilder sb = new StringBuilder();
if(this.north != null) sb.append(this.north.toString()).append(" North") else sb.append("No Exit for: North");
...
return sb.toString();
}
....
public class Room {
private String name;
...
#Override
public String toString() {
return this.name;
}
}
exit isn't something you can describe with a String. In the OO world, it should be a reference to a more meaningful object. I would go with
public Collection<Room> getExits();
or
public Map<String, Room> getExits();
which accurately describes where you can get from the hall. Here, we are assuming that "exit" is a doorway to another room.
You could return
Arrays.asList(northRoom, eastRoom, southRoom, westRoom);
or
Map<String, Room> map = new HashMap<>();
map.put("north", northRoom);
...
return map;
Then you would be able to provide any String representation from the returned collection.
It's like a sign placed in the hall to help people navigate. Even though it can be replaced with another sign (a more detailed/accurate one), the structure of the building is constant, and you aren't altering it. You are just representing it differently.
String simpleSign = "You can go to: " + getExits().stream().map(Object::toString).collect(Collectors.join(", "));
or
String detailedSign = "Directions to go: " + getExits().entrySet().stream().map(e -> e.getKey() + " -> " + e.getValue().toString()).collect(Collectors.join("\n"));
Here's one way of doing things. It's a little awkward because you have to check for null for each case - if that's not the case for you, you can remove those checks.
public String getExits (){
List<String> exits = new ArrayList<>();
if (north != null) exits.add("North: " + north.name);
if (south != null) exits.add("South: " + south.name);
if (east != null) exits.add("East: " + east.name);
if (west != null) exits.add("West: " + west.name);
return String.join("\n", exits);
}
Here's an article on Dozer: https://www.baeldung.com/dozer. It's a mapper that uses reflection to map same-name fields from one object to another (of a completely unrelated class).
I was wondering if this works flexibly with private fields, getters, and setters. That is,
Will private String a map to another object's private String a without either having any getters or setters?
What if only one side has a getter or setter (and the private field is named something different to make sure it's not directly accessing private fields)?
What if one has a getter and the other has a setter for totally mismatching private fields? (But the getter and setter names match.)
I wrote a test program to run in https://www.jdoodle.com/online-java-compiler:
import org.dozer.DozerBeanMapper;
public class Main {
public static class MySource {
// a -> a
private String a;
// getB() -> b
private String hidden_b;
public String getB() { return hidden_b; }
// c -> setC(c)
private String c;
// getD() -> setD(d)
private String hidden_d;
// proper getters and setters on both sides
private String proper;
public String getProper() { return proper; }
// public void setProper(String proper_) { proper = proper_; }
public MySource() {
a = "A Room with a View";
hidden_b = "The Bridge of San Luis Rey";
c = "Civilwarland in Bad Decline";
hidden_d = "Darkness at Noon";
proper = "This should copy, at minimum.";
}
public void print() {
System.out.println("Source");
System.out.println("================================");
System.out.println("a = " + a);
System.out.println("hidden_b = " + hidden_b);
System.out.println("c = " + c);
System.out.println("hidden_d = " + hidden_d);
System.out.println("--------------------------------");
System.out.println("proper = " + proper);
System.out.println("");
}
}
public static class MyTarget {
private String a;
private String b;
private String hidden_c;
private String hidden_e;
public void setC(String param) { hidden_c = param; }
public void setD(String param) { hidden_e = param; }
private String proper;
// public String getProper() { return proper; }
public void setProper(String proper_) { proper = proper_; }
public MyTarget() {}
public void print() {
System.out.println("Target");
System.out.println("================================");
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("hidden_c = " + hidden_c);
System.out.println("hidden_e = " + hidden_e);
System.out.println("--------------------------------");
System.out.println("proper = " + proper);
System.out.println("");
}
}
public static void main(String args[]) {
MySource s = new MySource();
s.print();
System.out.println("Now dozing...");
System.out.println("");
MyTarget t = new DozerBeanMapper().map(s, MyTarget.class);
t.print();
}
}
Note that to run the above code you must add a maven dependency:
Group ID: net.sf.dozer
Artifact ID: dozer
Version: 5.5.1
And also you must try executing a few times because of random timeouts depending on whether the dependency loads fast enough.
Anyway, my output was:
Source
================================
a = A Room with a View
hidden_b = The Bridge of San Luis Rey
c = Civilwarland in Bad Decline
hidden_d = Darkness at Noon
--------------------------------
proper = This should copy, at minimum.
Now dozing...
Target
================================
a = null
b = null
hidden_c = null
hidden_e = null
--------------------------------
proper = This should copy, at minimum.
So, it appears Dozer only works through a getter on the source and a setter on the target, which is disappointing. Or, I'm not using it correctly!
Is there a way to make Dozer more flexible? Or, another mapper library that can achieve this?
Okay, here are my findings. Hopefully this helps someone.
Dozer 5.5.1 was supposed to be able to do this via "class-level is-accessible." However, there was a bug. It was fixed for future releases, e.g. Dozer 6.1+. (The package moved to a new group, org.github.dozermapper.) The steps were a little complicated though, and eventually I gave up to try ModelMapper, which was much nicer. So here's my code.
Include this package:
Group ID: org.modelmapper
Artifact ID: modelmapper
Version: 2.3.2
Here's how to use it:
import org.modelmapper.ModelMapper;
import org.modelmapper.config.Configuration;
public class Main {
public static class MySource {
// a -> a
private String a;
// getB() -> b
private String hidden_b;
public String getB() { return hidden_b; }
// c -> setC(c)
private String c;
// getD() -> setD(d)
private String hidden_d;
// proper getters and setters on both sides
private String proper;
public String getProper() { return proper; }
// public void setProper(String proper_) { proper = proper_; }
public MySource() {
a = "A Room with a View";
hidden_b = "The Bridge of San Luis Rey";
c = "Civilwarland in Bad Decline";
hidden_d = "Darkness at Noon";
proper = "This should copy, at minimum.";
}
public void print() {
System.out.println("Source");
System.out.println("================================");
System.out.println("a = " + a);
System.out.println("hidden_b = " + hidden_b);
System.out.println("c = " + c);
System.out.println("hidden_d = " + hidden_d);
System.out.println("--------------------------------");
System.out.println("proper = " + proper);
System.out.println("");
}
}
public static class MyTarget {
private String a;
private String b;
private String hidden_c;
private String hidden_e;
public void setC(String param) { hidden_c = param; }
public void setD(String param) { hidden_e = param; }
private String proper;
// public String getProper() { return proper; }
public void setProper(String proper_) { proper = proper_; }
public MyTarget() {}
public void print() {
System.out.println("Target");
System.out.println("================================");
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("hidden_c = " + hidden_c);
System.out.println("hidden_e = " + hidden_e);
System.out.println("--------------------------------");
System.out.println("proper = " + proper);
System.out.println("");
}
}
public static void main(String args[]) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
MySource s = new MySource();
s.print();
System.out.println("Now dozing...");
System.out.println("");
MyTarget t = modelMapper.map(s, MyTarget.class);
t.print();
}
}
Here's my output:
Source
================================
a = A Room with a View
hidden_b = The Bridge of San Luis Rey
c = Civilwarland in Bad Decline
hidden_d = Darkness at Noon
--------------------------------
proper = This should copy, at minimum.
Now dozing...
Target
================================
a = A Room with a View
b = The Bridge of San Luis Rey
hidden_c = Civilwarland in Bad Decline
hidden_e = null
--------------------------------
proper = This should copy, at minimum.
The fourth case didn't copy over but I don't really care about that case. I think it can easily achieved with a different ModelMapper configuration though. Maybe try LOOSE copying. Or worst case, manually bind the getter and setter methods in the config.
Dozer by default uses getters and setters, however you can tell Dozer (via mapping) to access the fields directly
http://dozer.sourceforge.net/documentation/custommethods.html
BTW, Dozer 5 and 6 contains an API based mapping as well.
I have an Object ArrayList and I need to use the toString() method of the Motor object, which is a parameter of the Vehicle object. My vehicle objects are in an ArrayList which is iterated through with a for-loop (I know a foreach loop would be easier, but this is part of the assignment)
Here is the code for the loop:
for (int i = 0; i < VehicleList.size(); i++) {
System.out.println();
String info = VehicleList.get(i).toString();
Motor m = VehicleList.get(i).motor;
String motorInfo = m.toString();
System.out.println(info);
System.out.println(m);
}
There is an error that says "motor cannot be resolved or is not a field".
All of the classes should allow this to work, unless of course there is a simple mistake I am missing.
Here is the Motor class:
public class Motor {
protected String name;
protected int cylinders;
protected int bhp;
protected double displacement;
public Motor(String name, int cylinders, int bhp, double displacement) {
this.name = name;
this.cylinders = cylinders;
this.bhp = bhp;
this.displacement = displacement;
}
public String toString() {
return "Motor name= " + name + ", cylinders= " + cylinders + ", bhp=
" + bhp + ", displacement= " + displacement;
}
}
Motors and Vehicles are intitialized here (In the TestVehicle class):
//Motors
Motor EcoBoost = new Motor("EcoBoost", 6, 310, 2.3);
Motor Hemi = new Motor("Hemi", 8, 707, 5.7);
Motor P90D = new Motor("P90D", 0, 762, 0.0);
//Vehicles
Vehicle v0 = new PassCar("Ford", "Mustang", 2016, 44500.0, 5, true, EcoBoost);
Vehicle v1 = new PassCar("Tesla", "Model S", 2016, 121000.0, 2, true, P90D);
Vehicle v2= new Truck("Dodge", "Ram", 2016, 46000.0, "pickup", 1500, Hemi);
PassCar and Truck are inherited classes of Vehicle with a few more attributes. I can post the PassCar or Truck class if needed but I do not think that is where the problem is arising from. I believe it is coming from the For-Loop, specifically the line Motor m = VehicleList.get(i).motor; but I am not sure of how to fix it.
Vehicle Class:
public class Vehicle {
protected String make;
protected String model;
protected int year;
protected double price;
public Vehicle(String make, String model, int year, double price) {
this.make = make;
this.model = model;
this.year = year;
this.price = price;
}
public void description() {
System.out.println("Description");
}
public String toString() {
return "make= " + make + ", model= " + model + ", year= " + year +
", price= " + price;
}
}
EDIT: There cannot be any Getters or Setters as per the assignment requirements, and it must be an ArrayList, not a regular List. When I switch to I get the error "Type mismatch: cannot convert from ArrayList to ArrayList
Here is an image of the classes:
ArrayList<Object> VehicleList = new ArrayList<>(Arrays.asList(vehicles));
VehicleList is declared to contain instances of Object, so the compiler will only let you access methods and fields it knows exist on all instances of Object.
Change it to ArrayList<Vehicle>.
First, mind the naming convention. Variables should be named in camcelCase e.g. vehicleListinstead ofVehicleList`
I have an Object ArrayList
I believe you mean declaration of vehicleList looks like ArrayList<Object> vehicleList
Then behavior is expected because compiler only knows that VehicleList.get(i) is going to return you an Object reference. It can be a Vehicle, but it can also be anything else. So it won't allow you to access the motor field, as there is simply no such field in Object.
Change your declaration to something like List<Vehicle> vehicleList
However, as mentioned in other answer, it is not a good idea to access the field directly because of various reason. A slightly less evil way is to have getter of motor. (A better way is to provide meaningful behaviors instead of providing access to internal data)
Create an interface IMotor which is used by Vehicle class and Implemented in PassCar and other implementation of vehicle.
IMotor.java
public interface IMotor {
public Motor getMotor();
}
Motor.java
public class Motor {
protected String name;
protected int cylinders;
protected int bhp;
protected double displacement;
public Motor(String name, int cylinders, int bhp, double displacement) {
this.name = name;
this.cylinders = cylinders;
this.bhp = bhp;
this.displacement = displacement;
}
public String toString() {
return "Motor name= " + name + ", cylinders= " + cylinders + ", bhp=" + bhp + ", displacement= " + displacement;
}
}
Vehicle.java
public abstract class Vehicle implements IMotor{
protected String make;
protected String model;
protected int year;
protected double price;
public Vehicle(String make, String model, int year, double price) {
this.make = make;
this.model = model;
this.year = year;
this.price = price;
}
public String toString() {
return "make= " + make + ", model= " + model + ", year= " + year +
", price= " + price;
}
}
PassCar
public class PassCar extends Vehicle{
protected Motor motor;
public PassCar(String make, String model, int year, double price, Motor motor) {
super(make, model, year, price);
this.motor = motor;
}
public Motor getMotor() {
return motor;
}
}
Test.java
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) {
Motor EcoBoost = new Motor("EcoBoost", 6, 310, 2.3);
Vehicle v0 = new PassCar("Ford", "Mustang", 2016, 44500.0, EcoBoost);
List<Vehicle> vehicles = Arrays.asList(v0);
System.out.println(vehicles.get(0).getMotor());
}
}
Your problem is that motor is not a member of the Vehicle class, but you are trying to access it through an expression of type Vehicle - namely vehicleList.get(i). This is forbidden, because the compiler has no way of knowing that every possible kind of Vehicle has a motor. After all, what would happen if you added a Bicycle class?
To make this work, you should remove motor from the Truck and PassCar classes, and add it to the Vehicle class. That way, vehicleList.get(i).motor would actually make sense, since the Vehicle expression would be guaranteed to refer to a Vehicle with a Motor.
It would also be recommended to use a getter for the motor field - that is, have motor as a private field of the Vehicle class, and write a method getMotor() to return it. You could then write vehicleList.get(i).getMotor() to get the Motor object associated with one Vehicle in the list.
Thanks to the help of all of your comments and my Java textbook, I managed to piece it together. Here is how I got it to work:
for (int i = 0; i < vehicleList.size(); i++) {
String motorInfo = "";
String info = "";
System.out.println();
if (vehicleList.get(i) instanceof PassCar) {
info = ((PassCar)vehicleList.get(i)).toString();
**motorInfo = ((PassCar)vehicleList.get(i)).motor.toString();**
}
else if(vehicleList.get(i) instanceof Truck) {
info = ((Truck)vehicleList.get(i)).toString();
**motorInfo = ((Truck)vehicleList.get(i)).motor.toString();**
}
Basically I had to use a polymorphic call and check if it was an instance of a PassCar or Truck.
And as for the Array and ArrayList used during the Class, I edited them like this:
Vehicle [] vehicles = new Vehicle [3];
vehicles[0] = v0;
vehicles[1] = v1;
vehicles[2] = v2;
showVehicle(vehicles);
ArrayList<Vehicle> vehicleList = new ArrayList<Vehicle>(Arrays.asList(vehicles));
System.out.println();
System.out.println("Output from ArrayList in main: ");
Thank you for the help everyone!
I'm pretty new to Java 8 and want to rewrite some code.
Any ideas how to write this prettier?
The idea is, that one Person has many Units. One Unit has many requirements. We want to collect all requirements for each unit and multiply them with the amount the person has. In the end we want to output for each distinct unit the name and the amount.
Data example:
Person "a"
- has Unit "u1" 10 times
- has Unit "u2" 15 times
Unit u1
- requires Unit "u2" 3 times
- requires Unit "u3" 1 times
Unit u2
- requires Unit "u3" 3 times
The result should be:
Your reserved Units:
\#\# 30x u2
\#\# 55x u3
The code in Java 7 looks like this:
System.out.println("Your reserved Units: ");
final Map<Unit, Integer> allRequirements = new HashMap<Unit, Integer>();
// for each unit that is assigned to the person
for (UnitAmountPerson unitAmountPerson : person.getUnitAmounts()) {
// go through each unit that is a requirement for this unit
for (UnitAmountRequirements requirements : unitAmountPerson.getUnit().getRequirements()) {
// calculate the amount of requirements
final Unit requiredUnit = requirements.getUnit();
final int requiredAmount = unitAmountPerson.getAmount() * requirements.getAmount();
if (!allRequirements.containsKey(requiredUnit)) {
allRequirements.put(requiredUnit, 0);
}
allRequirements.put(requiredUnit, allRequirements.get(requiredUnit) + requiredAmount);
}
}
for (Entry<Unit, Integer> entry : allRequirements.entrySet()) {
System.out.println("## " + entry.getValue() + "x " + entry.getKey());
}`
The entities looks like this:
Person.java
public class Person{)
private Set<UnitAmountPerson> unitAmounts = new HashSet<UnitAmountPerson>();
public Set<UnitAmountPerson> getUnitAmounts() {
return unitAmounts;
}
}
Unit.java
public class Unit {
private String name;
private Set<UnitAmount> unitAmounts = new HashSet<UnitAmount>();
private Set<UnitAmountRequirements> requirements = new HashSet<UnitAmountRequirements>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<UnitAmountRequirements> getRequirements() {
return requirements;
}
public Set<UnitAmount> getUnitAmounts() {
return unitAmounts;
}
}
UnitAmount.java
public class UnitAmount {
private Unit unit;
private int amount;
public Unit getUnit() {
return unit;
}
public void setUnit(Unit unit) {
this.unit = unit;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
UnitAmountPerson.java
public class UnitAmountPerson extends UnitAmount {
private Person owner;
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
}
UnitAmountRequirement.java
public class UnitAmountRequirements extends UnitAmount {
private Unit owner;
public Unit getOwner() {
return owner;
}
public void setOwner(Unit owner) {
this.owner = owner;
}
}
You can have the following:
Map<Unit, Integer> allRequirements =
person.getUnitAmounts()
.stream()
.flatMap(unitAmountPerson ->
unitAmountPerson.getUnit()
.getRequirements()
.stream()
.map(r -> new AbstractMap.SimpleEntry<>(unitAmountPerson.getAmount(), r))
)
.collect(Collectors.toMap(
e -> e.getValue().getUnit(),
e -> e.getKey() * e.getValue().getAmount(),
Integer::sum
));
This code creates a Stream over the unit amount. For each one of them, we need to store its amount and the list of its requirements; for that, we flat map the stream to a Stream<Map.Entry> where the key is the amount for that unit amount and the value is the flat mapped requirement. We have to resort to using a temporary data holder like AbstractMap.SimpleEntry to hold those two values since there are no built-in tuple in the API.
Finally, this stream is collected to a Map where the key is the requirement's unit and the value is the product of the unit's amount and the requirement's amount. In case of duplicate values, the values are summed.