I have a class like followings.
public class Votes{
String name;
int likes;
int dislikes;
//constructors, getters and setters
}
I am having a list like followings.
List<Votes> votesList;
Assume I am populating above list with some elements. I want to declare a method which performs grouping and summing operation in that list.
As an example, assume I am giving the following elements in a list as the input
for that method.
votesList.add(new Votes("A", 10, 5));
votesList.add(new Votes("B", 15, 10));
votesList.add(new Votes("A", 20, 15));
votesList.add(new Votes("B", 10, 25));
votesList.add(new Votes("C", 10, 20));
votesList.add(new Votes("C", 0, 15));
That method should output a List<Votes>
with the following elements.
("A", 30, 20),
("B", 25, 35),
("C", 10, 35)
Is there an easy way to do that using streams, lambda expressions in Java8? I know how it can be done using collectors
if I only have one int
memeber.
Can someone please explain me how can I address this situation?
1 Java Lambda Expressions. Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. 2 Syntax. Expressions are limited. ... 3 Using Lambda Expressions. Lambda expressions can be stored in variables if the variable's type is an interface which has only one method.
Use Java's Consumer interface to store a lambda expression in a variable: To use a lambda expression in a method, the method should have a parameter with a single-method interface as its type. Calling the interface's method will run the lambda expression:
This code starts with a GroupBy clause with a Lambda expression. The Lambda expression (indicated with the => syntax in C# and the Function keyword in VB) uses the new keyword to create a new anonymous type with two properties: InvoiceDate and InvoiceType. This technique allows the code to group on both properties.
However, when a lambda expression uses a local variable from its enclosing scope, a special situation is created that is referred to as a variable capture. In this case, a lambda expression may only use local variables that are effectively final. An effectively final variable is one whose value does not change after it is first assigned.
One approach is to use groupingBy
, combined with reducing
:
Collection<Optional<Votes>> res =
votesList.stream().collect(groupingBy(v -> v.name, reducing((v1, v2) -> new Votes(v1.name, v1.likes + v2.likes, v1.dislikes + v2.dislikes)))).values();
This will give you a Collection<Optional<Votes>>
, you can get rid of it by combining the reducing
collector and the finisher function Optional::get
using the collectingAndThen
collector but that will starts to look quite hard to read.
So another alternative could be to use toMap
, and merge two votes whenever they have the same name:
Collection<Votes> res =
votesList.stream().collect(toMap(v -> v.name,
v -> v,
(v1, v2) -> new Votes(v1.name, v1.likes + v2.likes, v1.dislikes + v2.dislikes))).values();
From there you can use the ArrayList
copy-constructor (either by using collectingAndThen
with m -> new ArrayList<>(m.values())
, or by putting the previous expression in the parameter list of this copy constructor).
Finally, I came across this as the easiest way. :))
Map<String, List<Votes>> grouped = voteCountList.stream().collect(Collectors.groupingBy(r->r.getName()));
List<Votes> collectedList = new ArrayList<>();
grouped.forEach((groupName, votes) -> collectedList.add(new Votes(groupName,
votes.stream().collect(Collectors.summingInt(r->r.getLikes())),
votes.stream().collect(Collectors.summingInt(r->r.getDislikes())))));
This should get you started, it's not exactly what you want since it does not create the final list.
Map<String, Votes> collected = votesList.stream().collect(Collectors.groupingBy(Votes::getName,
collectingAndThen(reducing(
(originalVotes, newVotes) -> new Votes(originalVotes.getName(), originalVotes.getLikes() + newVotes.getLikes(), originalVotes.getDislikes() + newVotes.getDislikes()))
, Optional::get)));
collected.forEach((key, value) -> {
System.out.println(key + "," + value.getLikes() + "," + value.getDislikes());
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With