简体   繁体   中英

Java Stream API : How to use method reference?

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?

IntelliJ can literally just do this for you. You don't even have to think about it.

在此处输入图片说明

Keyboard shortcut for hints (yellow) is Alt Enter

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;
}

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()));

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()));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM