All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.util.stream.Collectors Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package java.util.stream;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.DoubleSummaryStatistics;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IntSummaryStatistics;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;

/**
 * See the
 * official Java API doc for details.
 */
public final class Collectors {
  public static  Collector averagingDouble(ToDoubleFunction mapper) {
    // TODO simplify to only collect average if possible
    return collectingAndThen(summarizingDouble(mapper), DoubleSummaryStatistics::getAverage);
  }

  public static  Collector averagingInt(ToIntFunction mapper) {
    // TODO simplify to only collect average if possible
    return collectingAndThen(summarizingInt(mapper), IntSummaryStatistics::getAverage);
  }

  public static  Collector averagingLong(ToLongFunction mapper) {
    // TODO simplify to only collect average if possible
    return collectingAndThen(summarizingLong(mapper), LongSummaryStatistics::getAverage);
  }

  public static  Collector collectingAndThen(
      Collector downstream, Function finisher) {
    return new CollectorImpl<>(
        downstream.supplier(),
        downstream.accumulator(),
        downstream.combiner(),
        downstream.finisher().andThen(finisher));
  }

  public static  Collector counting() {
    // Using Long::sum here fails in JDT
    return reducing(0L, item -> 1L, (a, b) -> (Long) a.longValue() + b.longValue());
  }

  public static  Collector>> groupingBy(
      Function classifier) {
    // TODO inline this and avoid the finisher extra work of copying from a map to another map
    //      kept separate for now to unify implementations and reduce testing required
    return groupingBy(classifier, toList());
  }

  public static  Collector> groupingBy(
      Function classifier, Collector downstream) {
    return groupingBy(classifier, HashMap::new, downstream);
  }

  public static > Collector groupingBy(
      Function classifier,
      Supplier mapFactory,
      Collector downstream) {
    return groupingBy0(() -> {
      // cannot use LinkedHashMap::new because javac cannot infer correct
      // return type of method reference
      return new LinkedHashMap<>();
    }, classifier, mapFactory, downstream);
  }

  private static > Collector groupingBy0(
      Supplier>> supplier,
      Function classifier,
      Supplier mapFactory,
      Collector downstream) {
    return Collector.of(
        supplier,
        (m, o) -> {
          K k = classifier.apply(o);
          List l = m.get(k);
          if (l == null) {
            l = new ArrayList<>();
            m.put(k, l);
          }
          l.add(o);

        },
        (m1, m2) -> mergeAll(m1, m2, Collectors::addAll),
        m -> {
          M result = mapFactory.get();
          for (Map.Entry> entry : m.entrySet()) {
            result.put(entry.getKey(), streamAndCollect(downstream, entry.getValue()));
          }
          return result;
        });
  }

//  not supported
//  public static  Collector>> groupingByConcurrent(
//      Function classifier)
//  public static  Collector> groupingByConcurrent(
//      Function classifier, Collector downstream)
//  public static > Collector groupingByConcurrent(
//      Function classifier, Supplier mapFactory,
//      Collector downstream)

  public static Collector joining() {
    // specific implementation rather than calling joining("") since we don't need to worry about
    // appending delimiters between empty strings
    return Collector.of(
        StringBuilder::new,
        StringBuilder::append,
        StringBuilder::append,
        StringBuilder::toString
    );
  }

  public static Collector joining(CharSequence delimiter) {
    return joining(delimiter, "", "");
  }

  public static Collector joining(
      final CharSequence delimiter, CharSequence prefix, CharSequence suffix) {
    return Collector.of(
        () -> new StringJoiner(delimiter, prefix, suffix),
        StringJoiner::add,
        StringJoiner::merge,
        StringJoiner::toString
    );
  }

  public static  Collector mapping(
      final Function mapper, final Collector downstream) {
    return new CollectorImpl<>(
        downstream.supplier(),
        (BiConsumer)
            (A a, T t) -> {
              downstream.accumulator().accept(a, mapper.apply(t));
            },
        downstream.combiner(),
        downstream.finisher());
  }

  public static  Collector> maxBy(Comparator comparator) {
    return reducing(BinaryOperator.maxBy(comparator));
  }

  public static  Collector> minBy(final Comparator comparator) {
    return reducing(BinaryOperator.minBy(comparator));
  }

  public static  Collector>> partitioningBy(
      Predicate predicate) {
    return partitioningBy(predicate, toList());
  }

  public static  Collector> partitioningBy(
      Predicate predicate, Collector downstream) {
    return groupingBy0(partitionSupplier(), predicate::test, HashMap::new, downstream);
  }

  private static  Supplier>> partitionSupplier() {
    return () -> {
      Map> partition = new LinkedHashMap<>();
      partition.put(false, new ArrayList<>());
      partition.put(true, new ArrayList<>());
      return partition;
    };
  }

  public static  Collector> reducing(BinaryOperator op) {
    return reducing(Optional.empty(), Optional::of, (a, b) -> {
      if (!a.isPresent()) {
        return b;
      }
      if (!b.isPresent()) {
        return a;
      }
      return Optional.of(op.apply(a.get(), b.get()));
    });
  }

  public static  Collector reducing(T identity, BinaryOperator op) {
    return reducing(identity, Function.identity(), op);
  }

  @SuppressWarnings("unchecked")
  public static  Collector reducing(
      final U identity, final Function mapper, BinaryOperator op) {
    return Collector.of(
      () -> new Object[]{identity},
      (u, t) -> u[0] = op.apply((U) u[0], mapper.apply(t)),
      (Object[] u1, Object[] u2) -> {
        u1[0] = op.apply((U) u1[0], (U) u2[0]);
        return u1;
      },
      (Object[] a) -> (U) a[0]
    );
  }

  public static  Collector summarizingDouble(
      ToDoubleFunction mapper) {
    return Collector.of(
        DoubleSummaryStatistics::new,
        (stats, item) -> stats.accept(mapper.applyAsDouble(item)),
        (t, u) -> {
          t.combine(u);
          return t;
        },
        Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
    );
  }

  public static  Collector summarizingInt(
      ToIntFunction mapper) {
    return Collector.of(
        IntSummaryStatistics::new,
        (stats, item) -> stats.accept(mapper.applyAsInt(item)),
        (t, u) -> {
          t.combine(u);
          return t;
        },
        Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
    );
  }

  public static  Collector summarizingLong(
      ToLongFunction mapper) {
    return Collector.of(
        LongSummaryStatistics::new,
        (stats, item) -> stats.accept(mapper.applyAsLong(item)),
        (t, u) -> {
          t.combine(u);
          return t;
        },
        Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
    );
  }

  public static  Collector summingDouble(final ToDoubleFunction mapper) {
    // TODO simplify to only collect sum if possible
    return collectingAndThen(summarizingDouble(mapper), DoubleSummaryStatistics::getSum);
  }

  public static  Collector summingInt(ToIntFunction mapper) {
    // TODO simplify to only collect sum if possible
    return collectingAndThen(
        summarizingInt(mapper), intSummaryStatistics -> (int) intSummaryStatistics.getSum());
  }

  public static  Collector summingLong(ToLongFunction mapper) {
    // TODO simplify to only collect sum if possible
    return collectingAndThen(summarizingLong(mapper), LongSummaryStatistics::getSum);
  }

  public static > Collector toCollection(
      final Supplier collectionFactory) {
    return Collector.of(
        collectionFactory,
        Collection::add,
        // TODO switch to a lambda reference once #9333 is fixed
        (c1, c2) -> addAll(c1, c2),
        Collector.Characteristics.IDENTITY_FINISH
    );
  }

//  not supported
//  public static  Collector> toConcurrentMap(
//      Function keyMapper, Function valueMapper)
//  public static  Collector> toConcurrentMap(
//      Function keyMapper, Function valueMapper,
//      BinaryOperator mergeFunction)
//  public static > Collector toConcurrentMap(
//      Function keyMapper, Function valueMapper,
//      BinaryOperator mergeFunction, Supplier mapSupplier)

  public static  Collector> toList() {
    return toCollection(ArrayList::new);
  }

  public static  Collector> toMap(
      final Function keyMapper,
      final Function valueMapper) {
    return toMap(
        keyMapper,
        valueMapper,
        (m1, m2) -> {
          throw new IllegalStateException("Can't assign multiple values to the same key");
        });
  }

  public static  Collector> toMap(
      Function keyMapper,
      Function valueMapper,
      BinaryOperator mergeFunction) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
  }

  public static > Collector toMap(
      final Function keyMapper,
      final Function valueMapper,
      final BinaryOperator mergeFunction,
      final Supplier mapSupplier) {
    return Collector.of(
        mapSupplier,
        (map, item) -> {
          K key = keyMapper.apply(item);
          U newValue = valueMapper.apply(item);
          if (map.containsKey(key)) {
            map.put(key, mergeFunction.apply(map.get(key), newValue));
          } else {
            map.put(key, newValue);
          }
        },
        (m1, m2) -> mergeAll(m1, m2, mergeFunction),
        Collector.Characteristics.IDENTITY_FINISH);
  }

  public static  Collector> toSet() {
    return Collector., Set>of(
        HashSet::new,
        HashSet::add,
        // TODO switch to a lambda reference once #9333 is fixed
        (c1, c2) -> addAll(c1, c2),
        // this is Function.identity, but Java doesn't like it here to change types.
        s -> s,
        Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
    );
  }

  private static  D streamAndCollect(Collector downstream, List list) {
    A a = downstream.supplier().get();
    for (T t : list) {
      downstream.accumulator().accept(a, t);
    }
    return downstream.finisher().apply(a);
  }

  private static > M mergeAll(
      M m1, M m2, BinaryOperator mergeFunction) {
    for (Map.Entry entry : m2.entrySet()) {
      m1.merge(entry.getKey(), entry.getValue(), mergeFunction);
    }
    return m1;
  }

  private static > C addAll(C collection, Collection items) {
    collection.addAll(items);
    return collection;
  }

  private Collectors() { }
}