How to customize a Collector

Collectors class Java 1.8 provides a lot of convenience interface, if existing interfaces can not meet the demand, it should be how to customize a Collector?


  Collector provides a default implementation, of the method can be by calling.

    public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
                                                 BiConsumer<A, T> accumulator,
                                                 BinaryOperator<A> combiner,
                                                 Function<A, R> finisher,
                                                 Characteristics... characteristics) {
        ...
        Set<Characteristics> cs = Collectors.CH_NOID;
        if (characteristics.length > 0) {
            cs = EnumSet.noneOf(Characteristics.class);
            Collections.addAll(cs, characteristics);
            cs = Collections.unmodifiableSet(cs);
        }
        return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);
    }
  • supplier provides input parameters for an object, for accumulation;
  • accumulator provides for an accumulation operation;
  • combiner for combining a plurality of input objects, only one input object case of an ordinary serial (Sequential) can be ignored, it must be combined when the object if the concurrent operation using steam (Parallel);
  • finisher means for calculating the final results into types that we need;
  • characteristics for optimizing the operation of the type specified;

  It is noted that input parameters mentioned Type Comment variable type must be provided (the mutable);

@param <A> the mutable accumulation type of the reduction operation (often hidden as an implementation detail)

  Let's look Collectors class source code is how to achieve Collector interface to common toList method, for example,

static final Set<Collector.Characteristics> CH_ID = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));

public static <T> Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, 
                               List::add,
                               (left, right) -> { 
                                    left.addAll(right); 
                                    return left; 
                               },
                               CH_ID);
}

  In toList method, as Supplier ArrayList :: new, provides a container for accumulating an ArrayList using List :: add accumulate operations as accumulator, called List of Combiner is achieved addAll merge two lists,
since the final type is List, Thus toList ignored finisher, using optimization IDENTITY_FINISH type indicated does not require a direct finisher operation returns the results.


  Assuming that there is a demand, the statistical requirements of the total number of A class list

 public class A {
    
    private int count;

    public A(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }
}

  Here the method can simply use summingInt Collectors mapToInt or offered by class.

var aList = new ArrayList<A>();
...
int totalUseMap = aList.stream().mapToInt(A::getCount).sum();
// or
int totalUseCollect = aList.stream().collect(Collectors.summingInt(Obj::getCount));

  If you use a custom Collector's words, how to achieve it?

var aList = new ArrayList<A>();
...
int total = aList.parallelStream().collect(Collector.of(() -> new int[1],
                                                (result, a) -> result[0] += a.getCount(),
                                                (a, b) -> {
                                                    a[0] += b[0];
                                                    return a;
                                                },
                                                result -> result[0],
                                                Collector.Characteristics.CONCURRENT));

  supplier must provide a variable object, there can not simply provide () -> 0, and as a result provides for the int type, int default input parameters are inconsistent [] types, set finisher must be converted to type int result,
since the accumulator It does not affect concurrency, so setting characteristics that support concurrent operations and improve performance.

Guess you like

Origin www.cnblogs.com/yeyu456/p/11989802.html