Java 8 Streams: How to call once the Collection.stream() method and retrieve an array of several aggregate values with different fields

后端 未结 3 470
没有蜡笔的小新
没有蜡笔的小新 2021-02-03 11:18

I\'m starting with the Stream API in Java 8.

Here is my Person object I use:

public class Person {

    private String firstName;
    private String last         


        
3条回答
  •  我在风中等你
    2021-02-03 11:55

    Without third-party libraries you may create a universal collector which combines the results of any number of specified collectors into single Object[] array:

    /**
     * Returns a collector which combines the results of supplied collectors
     * into the Object[] array.
     */
    @SafeVarargs
    public static  Collector multiCollector(
            Collector... collectors) {
        @SuppressWarnings("unchecked")
        Collector[] cs = (Collector[]) collectors;
        return Collector. of(
            () -> Stream.of(cs).map(c -> c.supplier().get()).toArray(),
            (acc, t) -> IntStream.range(0, acc.length).forEach(
                idx -> cs[idx].accumulator().accept(acc[idx], t)),
            (acc1, acc2) -> IntStream.range(0, acc1.length)
                .mapToObj(idx -> cs[idx].combiner().apply(acc1[idx], acc2[idx])).toArray(),
            acc -> IntStream.range(0, acc.length)
                .mapToObj(idx -> cs[idx].finisher().apply(acc[idx])).toArray());
    }
    

    For your concrete problem you'll also need a filtering() collector (which will be added in JDK-9, see JDK-8144675):

    public static  Collector filtering(
            Predicate filter, Collector downstream) {
        BiConsumer accumulator = downstream.accumulator();
        Set characteristics = downstream.characteristics();
        return Collector.of(downstream.supplier(), (acc, t) -> {
            if(filter.test(t)) accumulator.accept(acc, t);
        }, downstream.combiner(), downstream.finisher(), 
            characteristics.toArray(new Collector.Characteristics[characteristics.size()]));
    }
    

    Now you can build a collector which will generate the final result:

    Collector collector = 
        multiCollector(
            filtering(p -> p.getFirstName().equals("John"), counting()),
            collectingAndThen(mapping(Person::getAge, 
                maxBy(Comparator.naturalOrder())), Optional::get),
            collectingAndThen(mapping(Person::getHeight, 
                minBy(Comparator.naturalOrder())), Optional::get),
            averagingDouble(Person::getWeight));
    
    Object[] result = personsList.stream().collect(collector);
    System.out.println(Arrays.toString(result));
    

提交回复
热议问题