Reputation: 1658
There is code
class Person {
private ZonedDateTime date ;
private int regionId;
private int centerId;
private int amount1;
private float percent1;
}
List<Person> entityList = new ArrayList<>();
I grouping by year of month like this:
listPerson.stream()
.collect(Collectors.groupingBy(i -> i.getDate().getMonth(),Collectors.collectingAndThen(Collectors.toList(),
l -> {
Integer sumAmount1 = l.stream().collect(Collectors.summingInt(i -> i.getAmount1()));
Double avgPerc1 = l.stream().collect(Collectors.averagingDouble(i -> i.getPercent1()));
List<String> data = new ArrayList<>();
data.add(Integer.toString(sumAmount1));
data.add(Double.toString(avgPerc1));
return data;
}
))).forEach((k,v) -> System.out.println(k.getValue() + "-" + v.toString()));
Also i group by year, regionId, centerId in same manner:
listPerson.stream()
.collect(Collectors.groupingBy(i -> i.getDate().getYear(),Collectors ......
But i got many duplicate code where part with
l -> {...}
repeated many times. How to instead of l -> {...} use a method reference?
Upvotes: 0
Views: 241
Reputation: 3632
Might be a little of topic, but I think the beauty of using Stream API is allowing you to build a data pipeline. I would strive to build something that looks like a pipeline, with steps I can customize with pluggable functions.
I think the code would be more readable by refactoring towards a pipeline, and I would try below with the help of a new data structure called Tuple2
, epscially its map
method. It's easy to build, you can also use one from libraries like vavr
.
For reuse, one can consider a function like groupAndSummarize
(the name suggests it does two things, so is a smell).
class Tuple2<T1, T2> {
private final T1 t1;
private final T2 t2;
public Tuple2(final T1 t1, final T2 t2) {
this.t1 = t1;
this.t2 = t2;
}
public <U1, U2> Tuple2<U1, U2> map(final Function<T1, U1> func1,
final Function<T2, U2> func2) {
return new Tuple2<>(func1.apply(t1), func2.apply(t2));
}
public T1 _1() { return t1; }
public T2 _2() { return t2; }
}
private <T, K, V> List<Tuple2<K, V>> groupAndSummarize(final List<T> list, final Function<T, K> groupFn, final Function<List<T>, V> summarizeFn) {
return list.stream()
.collect(Collectors.groupingBy(groupFn))
.entrySet()
.stream()
.map(this::toTuple)
.map(t -> t.map(
Function.identity(),
summarizeFn
))
.collect(Collectors.toList());
}
private <K, V> Tuple2<K, V> toTuple(final Map.Entry<K, V> entry) {
return new Tuple2<>(entry.getKey(), entry.getValue());
}
private List<String> summarize(final List<Person> l) {
// your logic
}
public void test() {
final List<Person> entityList = new ArrayList<>();
groupAndSummarize(entityList, i -> i.getDate().getMonth(), this::summarize)
.forEach(t -> System.out.println(t.t1.getValue() + "-" + t.t2.toString()));
}
Upvotes: 1
Reputation: 44240
IntelliJ can literally just do this for you. You don't even have to think about it.
Keyboard shortcut for hints (yellow) is AltEnter
Here's what I ended up with
public static void main(String[] args)
{
List<Person> listPerson = null;
listPerson.stream()
.collect(Collectors.groupingBy(i -> i.getDate().getMonth(), Collectors.collectingAndThen(Collectors.toList(),
Scratch::apply
)))
.forEach((k,v) -> System.out.println(k.getValue() + "-" + v.toString()));
}
private static List<String> apply(List<Person> l)
{
int sumAmount1 = l.stream().mapToInt(Person::getAmount1).sum();
Double avgPerc1 = l.stream().collect(Collectors.averagingDouble(Person::getPercent1));
List<String> data = new ArrayList<>();
data.add(Integer.toString(sumAmount1));
data.add(Double.toString(avgPerc1));
return data;
}
Upvotes: 2
Reputation: 3866
You can create a method reference like this:
private List<String> methodReference(List<Person> l) {
Integer sumAmount1 = l.stream().collect(Collectors.summingInt(i -> i.getAmount1()));
Double avgPerc1 = l.stream().collect(Collectors.averagingDouble(i -> i.getPercent1()));
List<String> data = new ArrayList<>();
data.add(Integer.toString(sumAmount1));
data.add(Double.toString(avgPerc1));
return data;
}
I have created a methodReference
in my Test
class. You can replace it with your own class name. And now in your stream()
you can refer to it like this:
entityList.stream()
.collect(Collectors.groupingBy(i -> i.getDate().getMonth(), Collectors.collectingAndThen(Collectors.toList(),
Test::methodReference // replace Test with your class name
))).forEach((k, v) -> System.out.println(k.getValue() + "-" + v.toString()));
Upvotes: 1