Issue
@Data // lombok
public class Buzz {
private String key;
private Integer value;
// many other fields
}
@Data // lombok
public class Fizz {
private Long id;
private String name;
private List<Buzz> buzzes = Collections.emptyList();
// many other fields here
}
This data model is set and cannot be changed.
The application guarantees that each Fizz
instance has exactly zero (0) or one (1) Buzz
instance that has a key of "points". Meaning if a particular Fizz
instance has 10,000 Buzz
es in its list, either one and only one Buzz
will have a key of "points", or none will. I will never have 2+ Buzz
es with a key of "points".
I want to stream over the list of fizzes and if a Buzz
has a key = "points"
then I want to take its points value and sum it with the other "points Buzzes" in the list of Fizzes. If a Buzz key does not have a key of "points" we can default its value to zero (0).
Hence if there are two Fizzes like so:
Fizz f1 = new Fizz();
Fizz f2 = new Fizz();
Fizz f3 = new Fizz();
Buzz b1 = new Buzz();
Buzz b2 = new Buzz();
Buzz b3 = new Buzz();
Buzz b4 = new Buzz();
Buzz b5 = new Buzz();
Buzz b6 = new Buzz();
b1.setKey("flim");
b1.setValue(-1);
b2.setKey("flim");
b2.setValue(25);
b3.setKey("points");
b3.setValue(10);
b4.setKey("points");
b4.setValue(18);
b5.setKey("flim");
b5.setValue(-7);
b6.setKey("flim");
b6.setValue(100);
f1.setBuzzes(Arrays.asList(b1, b2, b3));
f2.setBuzzes(Arrays.asList(b4, b5));
f3.setBuzzes(Arrays.asList(b6));
List<Fizz> fizzes = Arrays.asList(f1, f2, f3);
Then I'm looking for a Stream-based solution that tallies up all the "points Buzzes". In this case the final value would be 28 since there are only 2 Buzzes in the entire list whose key = "points" (b3
and b4
) and their collective sum is 10 + 18 = 28.
My best attempt thus far:
Integer pointsSum = fizzes.stream()
.map(fizz -> fizz.getBuzzes().howDoItellIfItsBuzzHasAFizzKeyEqualToPoints())
.reduce(Integer::sum)
.orElse(0);
My problem is in crafting the map(...)
statement. How do I filter to see if the fizz
we are streaming over has a Buzz
whose key is equal to "points" so that we can map to its Buzz value
?
Solution
For that, you need to filter the Buzz
objects that have a key "points". And because you have that requirement:
one and only one Buzz will have a key of "points", or none will
That means that a single result is needed. For that purpose Stream IPA provides the findFirst()
method, which is a short-circuit operation (when it encounters the first element it returns it and no more elements from the stream will be processed).
Note that result may or may not be present in the stream therefore findFirst()
returns an object of type Optional. And .map(Buzz::getValue)
will be applied on an Optional result but not on the element of the stream.
int pointsSum = fizzes.stream()
.mapToInt(fizz -> getPoints(fizz))
.sum();
private static int getPoints(Fizz fizz) {
return fizz.getBuzzes().stream()
.filter(buzz -> buzz.getKey().equals("points"))
.findFirst()
.map(Buzz::getValue)
.orElse(0);
}
Note: call of getPoints()
can be written as a method reference in place of lambda, but I don't advise you to create a nested stream by putting all the code from this method into mapToInt()
. Functional programming was introduced in Java, not in order to write convoluted implementations but to reduce the complexity of code.
Answered By - Alexander Ivanchenko
Answer Checked By - Marilyn (JavaFixing Volunteer)