Stream of Java 8 — мощная операция сбора

задняя часть API дизайн

collectдолжен сказать даStreamСамая мощная из терминальных операций, и ее использование может быть практически произвольной агрегацией данных, которую вы хотите, и хороший анализ использования инструмента ниже.


В интерфейсе Stream есть два метода:

  <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

 <R, A> R collect(Collector<? super T, A, R> collector);

Очевидно, что первый эквивалентен простому варианту реализации, а второй — расширенному использованию.Все более сложные операции инкапсулируются в интерфейс Коллектора, а также предоставляются некоторые статические методы для вызова пользователями.Следующий анализ проводится один раз. одним.

Простая форма звонка

Простая форма вызова - это первый интерфейс, интерфейс выглядит следующим образом

  <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

Метод вызова следующий, очевидно, первый параметрsupplierКонтейнер для результата, второй параметрaccumulatorОперация добавления результата в контейнер, третий параметрcombinerЭто стратегия агрегации нескольких контейнеров.

String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,StringBuilder::append).toString();
//等价于上面,这样看起来应该更加清晰
String concat = stringStream.collect(() -> new StringBuilder(),(l, x) -> l.append(x), (r1, r2) -> r1.append(r2)).toString();

Потом по-другому, я хочу собрать сумму результатов для List.Согласно требованиям Collect, нам сначала нужна сумма контейнера, а затем добавить операцию sum+x, операцию агрегации, sum1+sum2, затем легко написать, прочитав следующий код После хорошего опыта, а затем посмотрите на расширенное использование.Конечно, использование метода суммы для сбора является лучшим решением, вот только пример приложения.

// 由于基本类型都是不可变类型,所以这里用数组当做容器
final Integer[] integers = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(() -> new Integer[]{0}, (a, x) -> a[0] += x, (a1, a2) -> a1[0] += a2[0]);

Итак, вернемся к одному изPersonкласс, который имеет два атрибута, тип и имя, затем используйтеcollectСоберите его в коллекцию Map, где ключ — тип, а значение — коллекция person Как показано в следующем коде, вы можете понять метод, когда увидите его, и поверите ему.

   Lists.<Person>newArrayList().stream()
        .collect(() -> new HashMap<Integer,List<Person>>(),
            (h, x) -> {
              List<Person> value = h.getOrDefault(x.getType(), Lists.newArrayList());
              value.add(x);
              h.put(x.getType(), value);
            },
            HashMap::putAll
        );

Collector Advanced Call.

CollectorИнтерфейс должен сделатьcollectМощное действующее абсолютное оружие для большинства операций можно разбить на основные этапы,Предоставление начального контейнера -> добавление элементов в контейнер -> агрегация нескольких контейнеров при одновременном выполнении -> работа с агрегированными результатами,в то же времяCollectorИнтерфейс также обеспечиваетofСтатические методы помогут вам настроить ваши операции в наибольшей степени, а официальный также предоставляетCollectorsЭтот вид упаковки наиболее распространен при инкассаторских операциях.
Кроме тогоCollectorImplзаCollectorКласс реализации, поскольку интерфейс не может быть создан, операция создания экземпляра в основном завершается здесь.

    //初始容器
     Supplier<A> supplier();
    //加入到容器操作
    BiConsumer<A, T> accumulator();
    //多容器聚合操作
    BinaryOperator<A> combiner();
    //聚合后的结果操作
    Function<A, R> finisher();
    //操作中便于优化的状态字段
    Set<Characteristics> characteristics();

Метод инкапсуляции коллекторов

CollectorsВ качестве официального класса инструмента коллекции многие из его операций носят справочный характер, что может помочь нам лучше понятьCollectorИнтерфейс неотделим от своей оригинальности, в конце концов, это просто смешанная работа пяти вышеперечисленных функциональных интерфейсов, давайте проанализируем, как чиновник использует эти интерфейсы.

toList()

контейнер:ArrayList::new
Добавленные контейнерные операции:List::add
Слияние нескольких контейнеров:left.addAll(right); return left;
Операция результата после агрегации: возвращается непосредственно здесь, поэтому такой операции нет, по умолчаниюcastingIdentity()
Поле состояния операции оптимизации:CH_ID
Выглядит это очень просто, поэтому для Map, Set и других операций аналогичны реализации.

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

joining()

контейнер:StringBuilder::new
Присоединиться к контейнерной операции:StringBuilder::append
Слияние нескольких контейнеров:r1.append(r2); return r1;
Результат операции после агрегации:StringBuilder::toString
Поле состояния операции оптимизации:CH_NOID

    public static Collector<CharSequence, ?, String> joining() {
        return new CollectorImpl<CharSequence, StringBuilder, String>(
                StringBuilder::new, StringBuilder::append,
                (r1, r2) -> { r1.append(r2); return r1; },
                StringBuilder::toString, CH_NOID);
    }

Вот сложный

groupingBy()

groupingByдаtoMapпродвинутый способ макияжаtoMapРазнообразные операции сбора не могут быть предоставлены для значений, таких как возвратMap<T,List<E>>такая формаtoMapТогда это не тоgroupingByОсновное внимание уделяется обработке и инкапсуляции значений Key и Value. Проанализируйте следующий код, гдеclassifierэто обработка ключевого значения,mapFactoryОн указан для указания конкретного типа контейнера для MAP.downstreamДля работы сбора ценности конкретный код не проанализирован здесь, не более, чем в указанный контейнер.

   public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream) {
       .......
    }

Для девственницы передcollectРабота сбора, выполненная методом, может быть легко переписана в качестве формы групповой группы

//原生形式
   Lists.<Person>newArrayList().stream()
        .collect(() -> new HashMap<Integer,List<Person>>(),
            (h, x) -> {
              List<Person> value = h.getOrDefault(x.getType(), Lists.newArrayList());
              value.add(x);
              h.put(x.getType(), value);
            },
            HashMap::putAll
        );
//groupBy形式
Lists.<Person>newArrayList().stream()
        .collect(Collectors.groupingBy(Person::getType, HashMap::new, Collectors.toList()));
//因为对值有了操作,因此我可以更加灵活的对值进行转换
Lists.<Person>newArrayList().stream()
        .collect(Collectors.groupingBy(Person::getType, HashMap::new, Collectors.mapping(Person::getName,Collectors.toSet())));

reducing()

reducingЭто коллекция для одного значения, и ее возвращаемый результат является не типом семейства коллекций, а одним классом сущностей T.
контейнер:boxSupplier(identity)Здесь завернута длина массива Object 1 [] , по причинам природы это неизменяемый тип pot
Присоединиться к контейнерной операции:a[0] = op.apply(a[0], t)
Слияние нескольких контейнеров:a[0] = op.apply(a[0], b[0]); return a;
Операция результата после агрегации: результатом, естественно, являются данные, обернутые Object[0]a -> a[0]
Поле состояния операции оптимизации:CH_NOID
Итак, когда я вижу здесь замешательство, есть ли у меня чувство внезапного осознания?

  public static <T> Collector<T, ?, T>
    reducing(T identity, BinaryOperator<T> op) {
        return new CollectorImpl<>(
                boxSupplier(identity),
                (a, t) -> { a[0] = op.apply(a[0], t); },
                (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
                a -> a[0],
                CH_NOID);
    }

Затем следующим шагом является преобразование некоторых операций из предыдущей команды Collect.

//原生操作
final Integer[] integers = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(() -> new Integer[]{0}, (a, x) -> a[0] += x, (a1, a2) -> a1[0] += a2[0]);
//reducing操作
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(Collectors.reducing(0, Integer::sum));    
//当然Stream也提供了reduce操作
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream().reduce(0, Integer::sum)

возможные проблемы

Запишите некоторые незначительные ошибки, возникшие при использовании инструмента в производственной среде.

исключение, сгенерированное toMap

Работа toMap в основном выглядит следующим образом, за исключением двух аспектов.

  1. Действие называетсяmap.mergeметод, этот метод будет сообщать npe, когда значение равно нулю, даже если вы используете hashMap, который может принимать нулевые значения, он все равно будет сообщать Я не понимаю, почему это разработано здесь.
  2. Стратегия слияния конфликта не указана, что является третьим параметромBinaryOperator<U> mergeFunctionКогда встречается дубликат ключа, он будет выброшен напрямуюIllegalStateExceptionТак что вам нужно обратить внимание.

Суммировать

до сих пор дляcollectОперация должна быть очень ясной, я надеюсь понять суть этих примеров, то естьCollectorРоль этих функций в интерфейсе, надеюсь, вам поможет.

личный блогmrdear.cn, добро пожаловать на обмен