Окончательный трюк Java 8 Stream — работа коллекторов

Spring Boot Java

1. Введение

вчера вОперация удаления элемента коллекцииупоминается в соответствующей статьеCollectors. Я считаю, что многих студентов это больше интересует, поэтому давайте сегодня изучим этоCollectors.

2. Роль коллекционеров

CollectorsдаJava 8Добавленный класс операции находится вjava.util.streamпод пакет. В нем будет обобщен сбор элементов по разным стратегиям, например, самый простой и распространенный — поместить элементы вMap,Set,Listи т. д. в изменяемом контейнере. специально дляJava 8 Stream Apiочень полезно. Это обеспечиваетcollect()путь кStreamПотоки выполняют операции завершения для получения наборов результатов на основе различных стратегий. мы полагаемся наStreamпознакомитьсяCollectorsБар. Мы по-прежнему используем вчерашний пример:

    List<String> servers = new ArrayList<>();
        servers.add("Felordcn");
        servers.add("Tomcat");
        servers.add("Jetty");
        servers.add("Undertow");
        servers.add("Resin");

3. Методы коллекторов в Java 8

CollectorsНам предоставляется ряд статических методов, обычно мы можем использовать статический импорт. Далее, давайте посмотрим, какие методы предусмотрены.

3.1 Тип индукции

Это серия, которая разделяет элементы на изменяемый контейнер.List,Map,Set,CollectionилиConcurrentMap.

    Collectors.toList();
    Collectors.toMap();
    Collectors.toSet();
    Collectors.toCollection();
    Collectors.toConcurrentMap();

Мы можем предоставить вышеAPIиспользоватьStreamизcollectПреобразование в методе к знакомому контейнеру-коллекции. Очень просто и не будет здесь демонстрироваться.

3.2 joining

Соединяйте элементы по некоторым правилам. Есть три перегрузки этого методаjoining(CharSequence delimiter)иjoining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

 //   输出 FelordcnTomcatJettyUndertowResin
 servers.stream().collect(Collectors.joining());

 //   输出 Felordcn,Tomcat,Jetty,Undertow,Resin
 servers.stream().collect(Collectors.joining("," ));

 //   输出 [Felordcn,Tomcat,Jetty,Undertow,Resin]
 servers.stream().collect(Collectors.joining(",", "[", "]")); 

Чаще используется для чтенияHttpServletRequestсерединаbody:

  HttpServletRequest.getReader().lines().collect(Collectors.joining());

3.3 collectingAndThen

Метод сначала выполняет операцию индукции, а затем выполняетFunctionФункция process выводит новый результат.

 // 比如我们将servers joining 然后转成大写,结果为: FELORDCN,TOMCAT,JETTY,UNDERTOW,RESIN   
 servers.stream.collect(Collectors.collectingAndThen(Collectors.joining(","), String::toUpperCase));

3.4 groupingBy

группировать элементы по условию иSQLсерединаgroup byИспользование имеет тот же эффект, и обычно рекомендуется использоватьJavaГрупповая обработка для снижения нагрузки на базу данных.groupingByТакже есть три перегруженных метода мы будемserversСгруппировать по длине:

// 按照字符串长度进行分组    符合条件的元素将组成一个 List 映射到以条件长度为key 的 Map<Integer, List<String>> 中
servers.stream.collect(Collectors.groupingBy(String::length))

если я не хочуMapизvalueзаListчто делать? Приведенная выше реализация фактически вызывает следующий метод:

 //Map<Integer, Set<String>>
 servers.stream.collect(Collectors.groupingBy(String::length, Collectors.toSet()))

Что, если мне придется учитывать вопросы безопасности синхронизации? Конечно, используйте потокобезопасный контейнер синхронизации, тогда первые два использовать нельзя! Не волнуйтесь! Давайте сделаем вывод, что второе на самом деле эквивалентно следующему написанию:

 Supplier<Map<Integer,Set<String>>> mapSupplier = HashMap::new;
 Map<Integer,Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));

Это очень легко сделать, мы предоставляем синхронизациюMapНет, это нормально, так что проблема решена:

 Supplier<Map<Integer, Set<String>>> mapSupplier = () -> Collections.synchronizedMap(new HashMap<>());
 Map<Integer, Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));

На самом деле вопросы безопасности синхронизацииCollectorsдругой методgroupingByConcurrentдает нам решение. использование иgroupingByпочти.

3.5 partitioningBy

partitioningByМы уже видели это в статье, упомянутой в начале этой статьи, которую можно рассматривать какgroupingByЧастный случай , основанный на утверждении (Predicate) группировка политик. Дополнительные примеры здесь не приводятся.

3.6 counting

Этот метод суммирует количество элементов, что очень просто и не будет проиллюстрировано.

3.7 maxBy/minBy

Эти два метода обеспечивают операции по нахождению элементов размера соответственно, и они основаны на интерфейсе компаратораComparatorдля сравнения, он возвращаетOptionalобъект. давайserversЭлемент с наименьшей длиной в:

 // Jetty  
Optional<String> min = servers.stream.collect(Collectors.minBy(Comparator.comparingInt(String::length)));

На самом деле здесьResinДлина также наименьшая, и здесь соблюдается принцип «предвзятости». КонечноStream.min()Очень удобно получить элемент минимальной длины.maxByТо же самое.

3.8 summingInt/Double/Long

Используется для кумулятивных расчетов. Вычислить сумму атрибута элемента, подобногоMysqlизsumФункции, такие как подсчет общей прибыли каждого проекта, подсчет суммы всех зарплат за месяц и так далее. Давайте посчитаем здесьserversСумма длин строк в (другие способы записи для примера не рассматриваются).

 // 总长度 32 
 servers.stream.collect(Collectors.summingInt(s -> s.length()));

3.9 summarizingInt/Double/Long

если мы правыРаздел 3.6 - Раздел 3.8Что делать с результатом операции? Может быть, мы делаем 5Streamпоток? Так что естьsummarizingInt,summarizingDouble,summarizingLongтри метода. Извлекая атрибут элемента, эти три метода возвращают объект статистических данных атрибута элемента, соответствующийIntSummaryStatistics,DoubleSummaryStatistics,LongSummaryStatistics. МыserversДлина элементов в статистике:

 DoubleSummaryStatistics doubleSummaryStatistics = servers.stream.collect(Collectors.summarizingDouble(String::length));
  // {count=5, sum=32.000000, min=5.000000, average=6.400000, max=8.000000}
  System.out.println("doubleSummaryStatistics.toString() = " + doubleSummaryStatistics.toString());

результатDoubleSummaryStatisticsсодержитВсего, Сумма, Мин., Макс., Среднеепять индикаторов.

3.10 mapping

Метод заключается в том, чтобы сначала использовать элементFunctionвыполнить операцию повторной обработки, а затем использовать другуюCollectorиндукция. Например, мы сначала удаляемserversпервая буква элементов вList.

 // [elordcn, omcat, etty, ndertow, esin]
 servers.stream.collect(Collectors.mapping(s -> s.substring(1), Collectors.toList()));

несколько похожеStreamсделано первымmapоперация сноваcollect:

 servers.stream.map(s -> s.substring(1)).collect(Collectors.toList());

3.11 reducing

Этот метод очень полезен! Но если вы хотите понять это, вы должны понять его параметрыBinaryOperator<T>. Это функциональный интерфейс, который дает две величины одного типа и возвращает результат того же типа, что и две величины.(T,T) -> T. Две реализации даны по умолчаниюmaxByиminBy, который сравнивает размер в соответствии с компаратором и возвращает максимальное или минимальное значение соответственно. Конечно, вы можете гибко настроить. потомreducingЭто легко понять. Сравнение между элементами основано на стратегии устранения одного. По ходу раунда количество элементовreduceиз. Так какая от этого польза? Чиновник Явы привел пример: посчитайте самого высокого человека в каждом городе.

  Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
     Map<String, Optional<Person>> tallestByCity = people.stream()
                          .collect(Collectors.groupingBy(Person::getCity, Collectors.reducing(BinaryOperator.maxBy(byHeight))));

В сочетании с примером, приведенным в начале, вы можете использоватьreducingПопробуйте найти самую длинную строку.

Слой выше основан наHeightНайдите высший атрибутPerson, и если это свойство не имеет начального значения или данных, то результат, скорее всего, не будет получен, поэтому заданоOptional<Person>. если мы дадимidentityСделайте эталонное значение, затем мы сначала проведем сравнение с этим эталонным значениемBinaryOperatorработать. Например, мы даем человеку ростом более 2 метров какidentity. Мы можем посчитать человека ростом не ниже 2 метров и самым высоким в каждом городе.Конечно, если в городе нет никого выше 2 метров, будет возвращено эталонное значение.identity:

 Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
 Person identity= new Person();
           identity.setHeight(2.);
           identity.setName("identity");
     Map<String, Person> collect = persons.stream()
                        .collect(Collectors.groupingBy(Person::getCity, Collectors.reducing(identity, BinaryOperator.maxBy(byHeight))));

В это время он обязательно вернетPersonНу, по крайней мере, это будет эталонное значениеidentityБольше неOptional.

В других случаях мы хотели быreducingкогдаPersonСначала округляется высота. Это требует от нас выполнения процесса сопоставления. определитьFunction<? super T, ? extends U> mapperчтобы сделать эту работу. Затем приведенную выше логику можно изменить на:

   Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
        Person identity = new Person();
        identity.setHeight(2.);
        identity.setName("identity");
        // 定义映射 处理 四舍五入
        Function<Person, Person> mapper = ps -> {
            Double height = ps.getHeight();

            BigDecimal decimal = new BigDecimal(height);
            Double d = decimal.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
            ps.setHeight(d);
            return ps;
        };
        Map<String, Person> collect = persons.stream()
                .collect(Collectors.groupingBy(Person::getCity, Collectors.reducing(identity, mapper, BinaryOperator.maxBy(byHeight))));

4. Резюме

Сегодня мыJava 8серединаCollectorsподробно объяснил. если вы знакомы сCollectorsОперационный поток будет более удобным. Конечно вJava 8ПослеJava 9иJava 12серединаCollectorsЕсть новые функции, и мы продолжим объяснять, когда будет время. быть в курсе!

关注公众号:Felordcn获取更多资讯

Личный блог: https://felord.cn