Новые функции Java8 Stream (расширенные)

Java

предисловие

Текст был включен в мой репозиторий GitHub, добро пожаловать, звезда:GitHub.com/bin39232820…
Лучшее время посадить дерево было десять лет назад, затем сейчас

болтовня

Сегодня я начал писать серию новых функций Java 8. Как бы сказать, в основном есть несколько новых вещей.

  • Лямбда-выражения
  • функциональный интерфейс
  • ссылка на метод
  • Стрим поток
  • Необязательный класс
  • ключевое слово по умолчанию

Основные функции этих четырех — упростить написание кода, повысить производительность и т. д., но это также принесет хлопоты в обслуживании, потому что люди, которые в этом не разбираются, будут очень уставшими, но писать действительно вкусно. планирую поговорить о названии. Сегодня поговорим о нашем потоке Stream. Вы можете обратиться к следующим ссылкам для предыдущих разделов.
🔥Внутренние классы в Java, о которых вы не знали
🔥 Дженерики Java, которые вы должны знать
🔥Java8 новые функции лямбда-выражения, функциональный интерфейс, ссылка на метод и ключевое слово по умолчанию
🔥Необязательный класс новых функций Java8
🔥Java8 новые функции Stream (Basic)
Вчера было введение, а сегодня я расскажу вам о некоторых продвинутых вещах.

Собиратель потоков

вводить

Ранее мы использовали collect(toList()) для создания списка в потоке. В реальном процессе разработки List — это структура данных, которую мы часто используем, но иногда мы также надеемся, что Stream можно преобразовать для генерации других значений, таких как Map или set, или даже настроить для генерации желаемой структуры данных.

Collect, также известный как сборщик, представляет собой структуру общего назначения для генерации комплексных значений из потоков. Просто передайте его методу сбора, так называемому методу преобразования, и он сгенерирует нужную структуру данных. Здесь следует упомянуть, что Collectors, библиотека инструментов, инкапсулирует соответствующие методы преобразования в этой библиотеке. Конечно, библиотека инструментов Collectors инкапсулирует только некоторые часто используемые сценарии, но если у вас есть особые потребности, вам нужно их настроить.

Очевидно, что список — это самая естественная структура данных, которую можно создать из потока, но иногда люди также хотят генерировать другие значения из потока, такие как карта или набор, или вы хотите, чтобы пользовательский класс абстрагировался от того, что вы хотите.

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

Коллектор (наиболее часто используемый)

Collector — это интерфейс операции сокращения переменных в Stream. Операции сокращения переменных включают в себя: накопление элементов в коллекцию, использование StringBuilder для объединения строк, вычисление статистики, связанной с элементом, такой как сумма, минимум, максимум или среднее значение и т. д. Коллекторы (коллекторы классов) предоставляют реализации многих общих изменяемых операций редукции.

Сначала посмотрим, как это выглядит

Collector принимает три универсальных параметра с соответствующими ограничениями на тип данных операции сокращения переменных:

  • T: Тип входного элемента
  • A: изменяемый тип накопления для операций редукции (часто скрыт как деталь реализации)
  • R: тип результата изменяемой операции уменьшения.

Интерфейс Collector объявляет 4 функции, которые выполняются совместно для накопления элементов в изменяемый контейнер результатов и, при необходимости, выполняют окончательное преобразование результата.

Характеристики — это класс перечисления в Collector, который объявляет три атрибута, такие как CONCURRENT, UNORDERED и IDENTITY_FINISH, для ограничения атрибутов Collector.

  • CONCURRENT: указывает, что этот сборщик поддерживает параллелизм, что означает, что в нескольких потоках аккумулятор может вызывать контейнер результатов.
  • UNORDERED: указывает, что сборщик не выполняется в том порядке, в котором вводятся элементы в потоке.
  • IDENTITY_FINISH: указывает, что финишер реализует функцию идентификации, которую можно игнорировать.

Основное использование коллекторов

Прежде всего, давайте разберемся, что могут сделать для нас коллекторы, такие как: группировка, сортировка (поддержка сортировки по нескольким полям), максимальное значение, минимальное значение и среднее значение. Короче говоря, мы использовали sql для завершения данных ранее. связанные операции могут быть выполнены коллекторами

Слишком много, ха-ха, давай помедленнее

Преобразовать в другие коллекции

Однако для связанных операций многих потоков, упомянутых выше, мы всегда генерируем коллекцию из Strea, например:

  • Существующий код написан для коллекций, поэтому вам нужно преобразовать поток в коллекцию и передать;
  • Выполнив серию связанных операций с коллекцией, вы, в конце концов, захотите сгенерировать значение;
  • При написании модульных тестов вам необходимо делать утверждения для конкретной коллекции.

Некоторые потоки могут быть преобразованы в коллекции, такие как упомянутый ранее toList, который создает экземпляр класса java.util.List. Конечно, существуют также toSet и toCollection, которые генерируют экземпляры классов Set и Collection соответственно. Это один из наиболее часто используемых

toList

List<Integer> collectList = Stream.of(1, 2, 3, 4)
        .collect(Collectors.toList());
System.out.println("collectList: " + collectList);
// collectList: [1, 2, 3, 4]

toSet

Set<Integer> collectSet = Stream.of(1, 2, 3, 4)
        .collect(Collectors.toSet());
System.out.println("collectSet: " + collectSet);
// collectSet: [1, 2, 3, 4]

toMap

Чтобы сгенерировать карту, нам нужно вызвать метод toMap. Поскольку в Map есть два значения Key и Value, метод обработки этого метода отличается от toSet, toList и т.д. toMap должен принимать как минимум два параметра, один из которых используется для генерации ключа, а другой — для генерации значения. Существует три варианта метода toMap:

Map<Integer, String> map = list.stream().collect(Collectors.toMap(Person::getId, Person::getName));  
 

Преобразовать в значение

Потоки можно конвертировать в значения с помощью collect. maxBy и minBy позволяют пользователю генерировать значение в определенном порядке.

  • averagingDouble: усреднение, тип элемента Stream — double
  • averagingInt: среднее значение, тип элемента Stream — int.
  • averagingLong: среднее значение, тип элемента Stream — длинный.
  • подсчет: количество элементов в потоке
  • maxBy: при указанных условиях самый большой элемент Stream
  • minBy: при указанных условиях наименьший элемент Stream
  • уменьшение: уменьшить операцию
  • summarizingDouble: состояние данных потока статистики (двойное), включая количество, минимум, максимум, сумму и среднее значение.
  • summarizingInt: состояние данных потока статистики (целое число), включая количество, минимум, максимум, сумму и среднее значение.
  • summarizingLong: Состояние данных потока статистики (полное), включая количество, минимум, максимум, сумму и среднее значение.
  • summingDouble: sum, тип элемента Stream — double
  • summingInt: sum, тип элемента Stream — int
  • summingLong: сумма, тип элемента Stream длинный

Например (просто попробуйте сами):

    public static void main(String[] args) {
        List<String> strings = Arrays.asList("六脉神剑", "大神", "小菜鸡", "交流群:549684836");
        Integer integer = strings.stream().filter(string -> string.length() <= 6).map(String::length).sorted().limit(2)
                .distinct().collect(Collectors.maxBy(Comparator.comparing(Integer::intValue))).orElse(0);
    
        System.out.println(integer);

    }
    //3

разделить блок данных

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

Но есть проблема с этой операцией. Во-первых, для выполнения двух операций фильтрации требуется два потока. Во-вторых, если операция фильтрации сложна, выполняя такую ​​операцию над каждым потоком, код также становится избыточным.

Здесь мы должны рассказать о методе partitioningBy в библиотеке Collectors, который берет поток и разделяет его на две части: с помощью объекта Predicate, указания условия и определения, к какой части должен принадлежать элемент, и возврата Map в список на основе логического значения. Следовательно, для элементов Списка, соответствующих ключу true, выполняются условия, указанные в объекте Predicate, аналогично элементы Списка, соответствующие ключу false, не удовлетворяют условиям, указанным в объекте Predicate.

    public static void main(String[] args) {

        Map<Boolean, List<Integer>> collectParti = Stream.of(1, 2, 3, 4)
                .collect(Collectors.partitioningBy(it -> it % 2 == 0));
        System.out.println("collectParti : " + collectParti);

    }
    //collectParti : {false=[1, 3], true=[2, 4]}

пакет данных

Группировка данных — это более естественный способ разделения данных.Вместо разделения данных на истинные и ложные части данные можно сгруппировать с произвольными значениями.

Вызовите метод collect Stream, передайте сборщик, groupingBy принимает функцию классификации, которая используется для группировки данных, точно так же, как partitioningBy, принимает Объект Predicate делит данные на истинную и ложную части. Используемый нами классификатор — это объект Function, такой же, как операция карты.

Map<Boolean, List<Integer>> collectGroup= Stream.of(1, 2, 3, 4)
            .collect(Collectors.groupingBy(it -> it > 3));
System.out.println("collectGroup : " + collectGroup);// 打印结果
// collectGroup : {false=[1, 2, 3], true=[4]}

нить

Collectors.joining собирает значения в Stream, этим методом можно удобно получить Stream в строку. Функция объединения принимает три параметра, представляющие разрешение (разделение элементов), префикс и суффикс.

String strJoin = Stream.of("1", "2", "3", "4")
        .collect(Collectors.joining(",", "[", "]"));
System.out.println("strJoin: " + strJoin);
// strJoin: [1,2,3,4]

Комплексное упражнение

Ранее мы узнали, что Collector — мощный и очень полезный инструмент. Разве не было бы сильнее, если бы они были объединены? Глядя на предыдущий пример, когда данные сгруппированы, мы получаем список сгруппированных данных collectGroup : {false=[1, 2, 3], true=[4]}. Если наши требования выше, нам не нужен сгруппированный список, достаточно получить номер сгруппированного списка.

// 分割数据块
Map<Boolean, List<Integer>> collectParti = Stream.of(1, 2, 3, 4)
        .collect(Collectors.partitioningBy(it -> it % 2 == 0));

Map<Boolean, Integer> mapSize = new HashMap<>();
collectParti.entrySet()
        .forEach(entry -> mapSize.put(entry.getKey(), entry.getValue().size()));

System.out.println("mapSize : " + mapSize);
// mapSize : {false=2, true=2}

В методе partitioningBy есть такой вариант:

Map<Boolean, Long> partiCount = Stream.of(1, 2, 3, 4)
        .collect(Collectors.partitioningBy(it -> it.intValue() % 2 == 0,
                Collectors.counting()));
System.out.println("partiCount: " + partiCount);
// partiCount: {false=2, true=2}

Многоуровневая группировка: вы можете использовать группировку по вложенным

List<String> views = Lists.newArrayList("wsbsq","hello word","b8fw", "word", "wall", "ad");

Map<Object, Map<Object, List<String>>> res = views.stream().collect(groupingBy(str ->str.charAt(0),groupingBy(String::length)));

System.out.println(res);

// {a={2=[ad]}, b={4=[b8fw]}, w={4=[word, wall], 5=[wsbsq]}, h={10=[hello word]}}

Возьмите минимальное значение после группировки

       List<Integer> list= Arrays.asList(1,1,2,2,3,5,7,10,11,11,12);

        Map<Integer, Optional<Integer>> collect = list.stream().collect(groupingBy(Integer::intValue, Collectors.minBy(Comparator.comparing(Integer::intValue))));
        collect.forEach((key,value)->{
            System.out.println("key"+key+"      "+"value"+value);
        });

      //key1      valueOptional[1]
      //key2      valueOptional[2]
      //key3      valueOptional[3]
      //key5      valueOptional[5]
      //key7      valueOptional[7]
      //key10      valueOptional[10]
      //key11      valueOptional[11]
      //key12      valueOptional[12]

метод уменьшения в потоке

Личное понимание API

Метод сокращения имеет три перегруженных метода, сигнатуры методов следующие:

первый метод

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

Optional<T> reduce(BinaryOperator<T> accumulator);

пример

List<Integer> numList = Arrays.asList(1,2,3,4,5);
int result = numList.stream().reduce((a,b) -> a + b ).get();
System.out.println(result);

второй способ

Единственное отличие от реализации первого метода состоит в том, что при его первом выполнении первый параметр выражения не является первым элементом потока, а задается идентификатором первого параметра сигнатуры. Улучшим предыдущий код суммирования с помощью этой сигнатуры

T reduce(T identity, BinaryOperator<T> accumulator);

пример

List<Integer> numList = Arrays.asList(1,2,3,4,5);
int result = numList.stream().reduce(0,(a,b) ->  a + b );
System.out.println(result);

На самом деле эти две реализации почти различны: в первой всего на одно слово больше, чем в первой, определяющей начальное значение. Кроме того, поскольку есть случай, когда поток пуст, первая реализация не вычисляет результат метода напрямую, а оборачивает результат вычисления в необязательный.Мы можем получить результат целочисленного типа через его метод get, а целочисленный допускает ноль. Вторая реализация позволяет указать начальное значение, поэтому даже если поток пуст, возвращаемый результат не будет нулевым.Когда поток пуст, reduce возвращает начальное значение напрямую.

третий метод

Использование третьего метода немного сложнее, чем первых двух.Поскольку первые две реализации имеют изъян, результаты их вычислений должны совпадать с типом элемента в потоке.Как и в приведенном выше примере кода, тип в поток int, то результат вычисления тоже должен быть int, что приводит к отсутствию гибкости, и даже некоторые задачи не могут быть выполнены.Суммируем ряд значений int, но результат суммы уже не может вписывается в тип int, поэтому мы должны Upgrade в тип long, и третья сигнатура этого факта может играть свое значение, она не привязывает результат выполнения к типу элемента в потоке.

<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

пример

List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
ArrayList<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
    a.add("element-" + Integer.toString(b));
    return a;
}, (a, b) -> null);
System.out.println(result);
//[element-1, element-2, element-3, element-4, element-5, element-6]

Класс инструментов времени на основе JDK 1.8

Из-за новых возможностей 1.8 я в принципе закончил. Есть еще одна обработка времени Обработка времени в Java8 в основном LocalDate, LocalTime, LocalDateTime А как насчет меня? Я не планировал рассказывать все это, это относительно просто, я дам вам один из наших классов инструментов, который можно использовать, когда вы его используете.

package com.hq.eos.utils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class DateUtil {
    private static final String HYPHEN = "-";
    private static final String COLON = ":";

    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 时间格式 DateTimeFormatter (Java8) ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
    enum FormatEnum {
        /**
         * 返回 DateTimeFormatter "yyyy-MM-dd HH:mm:ss" 时间格式
         */
        FORMAT_DATA_TIME(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)),

        /**
         * 返回 DateTimeFormatter "yyyyMMddHHmmss"的时间格式
         */
        FORMAT_DATA_TIME_NO_SYMBOL(DateTimeFormatter.ofPattern(DATETIME_FORMAT)),

        /**
         * 返回 DateTimeFormatter "yyyy-MM-dd"的时间格式
         */
        FORMAT_DATE(DateTimeFormatter.ofPattern(DATE_FORMAT)),

        /**
         * 返回 DateTimeFormatter "HH:mm:ss"的时间格式
         */
        FORMAT_TIME(DateTimeFormatter.ofPattern(TIME_FORMAT));

        private DateTimeFormatter value;

        FormatEnum(DateTimeFormatter format) {
            this.value = format;
        }
    }
    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 时间格式 DateTimeFormatter (Java8) ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/

    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 时间格式 字符串 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * 年的时间格式
     * <br/>
     * 返回 "yyyy" 字符串
     */
    public static final String YEAR_FORMAT = "yyyy";

    /**
     * 月的时间格式
     * <br/>
     * 返回 "MM" 字符串
     */
    public static final String MONTH_FORMAT = "MM";

    /**
     * 日的时间格式
     * <br/>
     * 返回 "dd" 字符串
     */
    public static final String DAY_FORMAT = "dd";

    /**
     * 时的时间格式
     * <br/>
     * 返回 "HH" 字符串
     */
    public static final String HOUR_FORMAT = "HH";

    /**
     * 分的时间格式
     * <br/>
     * 返回 "mm" 字符串
     */
    public static final String MINUTE_FORMAT = "mm";

    /**
     * 秒的时间格式
     * <br/>
     * 返回 "ss" 字符串
     */
    public static final String SECOND_FORMAT = "ss";

    /**
     * <span color='red'>年-月-日</span>的时间格式
     * <br/>
     * 返回 "yyyy-MM-dd" 字符串
     */
    public static final String DATE_FORMAT = YEAR_FORMAT + HYPHEN + MONTH_FORMAT + HYPHEN + DAY_FORMAT;

    /**
     * <span color='red'>时:分:秒</span>的时间格式
     * <br/>
     * 返回 "HH:mm:ss" 字符串
     */
    public static final String TIME_FORMAT = HOUR_FORMAT + COLON + MINUTE_FORMAT + COLON + SECOND_FORMAT;

    /**
     * <span color='red'>年-月-日 时:分:秒</span>的时间格式
     * <br/>
     * 返回 "yyyy-MM-dd HH:mm:ss" 字符串
     */
    public static final String DATE_TIME_FORMAT = DATE_FORMAT + " " + TIME_FORMAT;

    /**
     * <span color='red'>年月日时分秒</span>的时间格式(无符号)
     * <br/>
     * 返回 "yyyyMMddHHmmss" 字符串
     */
    public static final String DATETIME_FORMAT = YEAR_FORMAT + MONTH_FORMAT + DAY_FORMAT + HOUR_FORMAT + MINUTE_FORMAT + SECOND_FORMAT;

    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 时间格式 字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/



    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 时间戳 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * 获取秒级时间戳
     */
    public static Long epochSecond() {
        return localDateTime().toEpochSecond(ZoneOffset.of("+8"));
    }

    /**
     * 获取毫秒级时间戳
     */
    public static Long epochMilli() {
        return localDateTime().toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }

    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 时间戳 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/


    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 当前时间相关 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * 获取当前详细时间,like 2018-08-27 17:20:06
     */
    public static String dateTime() {
        return localDateTime().format(FormatEnum.FORMAT_DATA_TIME.value);
    }

    /**
     * 获取当前详细时间,like 20180827172006
     */
    public static String dateTimeNoSymbol() {
        return localDateTime().format(FormatEnum.FORMAT_DATA_TIME_NO_SYMBOL.value);
    }

    /**
     * 获取当前日期,like 2018-08-27
     */
    public static String date() {
        return localDate() + "";
    }

    /**
     * 获取当前时间,like 17:20:06
     */
    public static String time() {
        return localTime().format(FormatEnum.FORMAT_TIME.value);
    }

    /**
     * 获取当前年
     */
    public static Integer year() {
        return localDate().getYear();
    }

    /**
     * 获取当前月
     */
    public static int month() {
        return localDate().getMonthValue();
    }

    /**
     * 获取当前年中的日
     */
    public static Integer dayOfYear() {
        return localDate().getDayOfYear();
    }

    /**
     * 获取当前月中的日
     */
    public static Integer dayOfMonth() {
        return localDate().getDayOfMonth();
    }

    /**
     * 获取当前星期中的日
     */
    public static Integer dayOfWeek() {
        return localDate().getDayOfWeek().getValue();
    }

    /**
     * 获取当前小时
     */
    public static Integer hour() {
        return localTime().getHour();
    }

    /**
     * 获取当前分钟
     */
    public static Integer minute() {
        return localTime().getMinute();
    }

    /**
     * 获取当前秒
     */
    public static Integer second() {
        return localTime().getSecond();
    }

    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 当前时间相关 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/



    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 未来、历史时间相关 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * 获取当前年的 前几年 的日期
     * <p>
     *
     * @param years  前几年 正整数
     * @param formatEnum 格式
     * @return 当前年的 前几年 的 对应 格式 日期
     */
    public static String minusYears(Long years, FormatEnum formatEnum) {
        return minusOrPlusYears(-years, formatEnum);
    }

    /**
     * 获取当前年的 后几年 的日期
     * <p>
     *
     * @param years  后几年 正整数
     * @param formatEnum 格式
     * @return 当前年的 后几年 的 对应 格式 日期
     */
    public static String plusYears(Long years, FormatEnum formatEnum) {
        return minusOrPlusYears(years, formatEnum);
    }

    /**
     * 获取当前月的 前几月 日期
     *
     * @param months     前几月 正整数
     * @param formatEnum 格式
     * @return 当前月的 前几月 的 对应 格式 日期
     */
    public static String minusMonths(Long months, FormatEnum formatEnum) {
        return minusOrPlusMonths(-months, formatEnum);
    }

    /**
     * 获取当前月的 后几月 的日期
     *
     * @param months     后几月 正整数
     * @param formatEnum 格式
     * @return 当前月的 后几月 的 对应 格式 日期
     */
    public static String plusMonths(Long months, FormatEnum formatEnum) {
        return minusOrPlusMonths(months, formatEnum);
    }

    /**
     * 获取当前日的 前几日 的日期
     *
     * @param days       前几日 正整数
     * @param formatEnum 格式
     * @return 当前日的 前几日 的 对应 格式 日期
     */
    public static String minusDays(Long days, FormatEnum formatEnum) {
        return minusOrPlusDays(-days, formatEnum);
    }

    /**
     * 获取当前日的 后几日 的日期
     *
     * @param days       后几日 正整数
     * @param formatEnum 格式
     * @return 当前日的 后几日 的 对应 格式 日期
     */
    public static String plusDays(Long days, FormatEnum formatEnum) {
        return minusOrPlusDays(days, formatEnum);
    }

    /**
     * 获取当前星期的 前几星期 的日期
     *
     * @param weeks      前几星期 正整数
     * @param formatEnum 格式
     * @return 当前星期的 前几星期 的 对应 格式 日期
     */
    public static String minusWeeks(Long weeks, FormatEnum formatEnum) {
        return minusOrPlusWeeks(-weeks, formatEnum);
    }

    /**
     * 获取当前星期的 后几星期 的日期
     *
     * @param weeks      后几星期 正整数
     * @param formatEnum 格式
     * @return 当前星期的 后几星期 的 对应 格式 日期
     */
    public static String plusWeeks(Long weeks, FormatEnum formatEnum) {
        return minusOrPlusWeeks(weeks, formatEnum);
    }

    /**
     * 获取当前小时的 前几小时 的日期
     *
     * @param hours      前几小时 正整数
     * @param formatEnum 格式
     * @return 当前小时的 前几小时 的 对应 格式 日期
     */
    public static String minusHours(Long hours, FormatEnum formatEnum) {
        return minusOrPlusHours(-hours, formatEnum);
    }

    /**
     * 获取当前小时的 后几小时 的日期
     *
     * @param hours      后几小时 正整数
     * @param formatEnum 格式
     * @return 当前小时的 后几小时 的 对应 格式 日期
     */
    public static String plusHours(Long hours, FormatEnum formatEnum) {
        return minusOrPlusHours(hours, formatEnum);
    }

    /**
     * 获取当前分钟的 前几分钟 的日期
     *
     * @param minutes    前几分钟 正整数
     * @param formatEnum 格式
     * @return 当前分钟的 前几分钟 的 对应 格式 日期
     */
    public static String minusMinutes(Long minutes, FormatEnum formatEnum) {
        return minusOrPlusMinutes(-minutes, formatEnum);
    }

    /**
     * 获取当前分钟的 后几分钟 的日期
     *
     * @param minutes    后几分钟 正整数
     * @param formatEnum 格式
     * @return 当前分钟的 后几分钟 的 对应 格式 日期
     */
    public static String plusMinutes(Long minutes, FormatEnum formatEnum) {
        return minusOrPlusMinutes(minutes, formatEnum);
    }

    /**
     * 获取当前秒的 前几秒 的日期
     *
     * @param seconds    前几秒 正整数
     * @param formatEnum 格式
     * @return 当前秒的 前几秒 的 对应 格式 日期
     */
    public static String minusSeconds(Long seconds, FormatEnum formatEnum) {
        return minusOrPlusSeconds(-seconds, formatEnum);
    }

    /**
     * 获取当前秒的 前几秒/后几秒 的日期
     *
     * @param seconds    后几秒 正整数
     * @param formatEnum 格式
     * @return 当前秒的 后几秒 的 对应 格式 日期
     */
    public static String plusSeconds(Long seconds, FormatEnum formatEnum) {
        return minusOrPlusSeconds(seconds, formatEnum);
    }

    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 未来、历史时间相关 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/


    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 时间转换相关 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * Date类型转LocalDateTime
     * <p>
     *
     * @param date date类型时间
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * Date类型转LocalDate
     * <p>
     *
     * @param date date类型时间
     * @return LocalDate
     */
    public static LocalDate toLocalDate(Date date) {
        return toLocalDateTime(date).toLocalDate();
    }

    /**
     * Date类型转LocalTime
     * <p>
     *
     * @param date date类型时间
     * @return LocalTime
     */
    public static LocalTime toLocalTime(Date date) {
        return toLocalDateTime(date).toLocalTime();
    }

    /**
     * LocalDateTime 类型转 Date
     *
     * @param localDateTime localDateTime
     * @return 转换后的Date类型日期
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDate类型转Date
     *
     * @param localDate localDate
     * @return 转换后的Date类型日期
     */
    public static Date toDate(LocalDate localDate) {
        return toDate(localDate.atStartOfDay());
    }

    /**
     * LocalTime类型转Date
     *
     * @param localTime localTime
     * @return 转换后的Date类型日期
     */
    public static Date toDate(LocalTime localTime) {
        return toDate(LocalDateTime.of(localDate(), localTime));
    }

    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 时间转换相关 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/


    /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 时间间隔相关 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * 获取 endDate-startDate 时间间隔天数
     * <br>创建人: leigq
     * <br>创建时间: 2018-11-07 09:55
     * <br>
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 时间间隔天数
     */
    public static Long daysInterval(LocalDate startDate, LocalDate endDate) {
        return endDate.toEpochDay() - startDate.toEpochDay();
    }

    /**
     * 获取 endDate-startDate 时间间隔天数
     * <br>创建人: leigq
     * <br>创建时间: 2018-11-07 09:55
     * <br>
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 时间间隔天数
     */
    public static Long daysInterval(String startDate, String endDate) {
        return daysInterval(LocalDateTime.parse(endDate, FormatEnum.FORMAT_DATA_TIME.value).toLocalDate(),
                LocalDateTime.parse(startDate, FormatEnum.FORMAT_DATA_TIME.value).toLocalDate());
    }

    /**
     * 获取 endDate-startDate 时间间隔天数
     * <br>创建人: leigq
     * <br>创建时间: 2018-11-07 09:55
     * <br>
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 时间间隔天数
     */
    public static Long daysInterval(LocalDateTime startDate, LocalDateTime endDate) {
        return daysInterval(startDate.toLocalDate(), endDate.toLocalDate());
    }

    /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 时间间隔相关 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/

    /*↓↓↓只允许此类调用↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/

    /**
     * 获取 当前年 的前几年/后几年的日期
     * <p>
     *
     * @param yearsToAddOrSubtract 后几年传正整数,前几年传负数
     * @param formatEnum           格式
     * @return 当前年的前几年/后几年的对应 格式 日期
     */
    private static String minusOrPlusYears(Long yearsToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusYears(yearsToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取 当前月 的前几月/后几月的日期
     *
     * @param monthsToAddOrSubtract 后几月传正整数,前几月传负数
     * @param formatEnum            格式
     * @return 当前月的前几月/后几月的对应 格式 日期
     */
    private static String minusOrPlusMonths(Long monthsToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusMonths(monthsToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取 当前日 的前几日/后几日的日期
     *
     * @param daysToAddOrSubtract 后几日传正整数,前几日传负数
     * @param formatEnum          格式
     * @return 当前日的前几日/后几日的 对应 格式 日期
     */
    private static String minusOrPlusDays(Long daysToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusDays(daysToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取当前星期的前几星期/后几星期的日期
     *
     * @param weeksToAddOrSubtract 后几星期传正整数,前几星期传负数
     * @param formatEnum           格式
     * @return 当前星期的前几星期/后几星期的 对应 格式 日期
     */
    private static String minusOrPlusWeeks(Long weeksToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusWeeks(weeksToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取当前小时的前几小时/后几小时的日期
     *
     * @param hoursToAddOrSubtract 后几小时传正整数,前几小时传负数
     * @param formatEnum           格式
     * @return 当前小时的前几小时/后几小时的 对应 格式 日期
     */
    private static String minusOrPlusHours(Long hoursToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusHours(hoursToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取当前分钟的前几分钟/后几分钟的日期
     *
     * @param minutesToAddOrSubtract 后几分钟传正整数,前几分钟传负数
     * @param formatEnum             格式
     * @return 当前分钟的前几分钟/后几分钟的 对应 格式 日期
     */
    private static String minusOrPlusMinutes(Long minutesToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusMinutes(minutesToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取当前秒的前几秒/后几秒的日期
     *
     * @param secondsToAddOrSubtract 后几秒传正整数,前几秒传负数
     * @param formatEnum             格式
     * @return 当前秒的前几秒/后几秒的 对应 格式 日期
     */
    private static String minusOrPlusSeconds(Long secondsToAddOrSubtract, FormatEnum formatEnum) {
        return localDateTime().plusSeconds(secondsToAddOrSubtract).format(formatEnum.value);
    }

    /**
     * 获取 LocalDate
     */
    private static LocalDate localDate() {
        return localDateTime().toLocalDate();
    }

    /**
     * 获取 LocalTime
     */
    private static LocalTime localTime() {
        return localDateTime().toLocalTime();
    }

    /**
     * 获取 LocalDateTime
     */
    private static LocalDateTime localDateTime() {
        return LocalDateTime.now();
    }

}

конец

Расширенное использование Stream также подошло к концу. На самом деле, блог, в лучшем случае, даст вам знать об этом. Вам нужно больше думать в проекте и использовать его, и практика делает совершенным. Я чувствую, что Карта, Уменьшение и MR больших данных очень похожи, и идея разделяй и властвуй. Ха-ха, это немного далеко, и это все еще Java-фундамент, но он все еще связан с большими данными, но давайте вместе потихоньку накапливать, мы поговорим об этом.

ежедневные комплименты

Хорошо всем, вышеизложенное является полным содержанием этой статьи. Люди, которые могут видеть это здесь, всеталант.

Творить нелегко. Ваша поддержка и признание — самая большая мотивация для моего творчества. Увидимся в следующей статье.

Six Meridians Excalibur | Text [Original] Если в этом блоге есть какие-то ошибки, прошу покритиковать и посоветовать, буду очень признателен!