Java8 Stream, простая и быстрая обработка коллекций (включено)

Java задняя часть база данных Shiro

Прошло несколько лет с тех пор, как была выпущена Java 8, и теперь Java перешла на 11, но изменения, внесенные в Java 8, можно назвать революционными и достаточно далеко идущими, чтобы изучение Java 8 должно было стать обязательным курсом для Java-разработчиков.

Сегодня я предлагаю вам объяснение потока Java 8. Почему я говорю об этом напрямую, так это то, что, как только вы закончите изучение, вы можете сразу же приступить к работе и заставить его работать в своем коде.

Стоит отметить, что вы должны узнать о lambda, прежде чем изучать Stream. В этой статье также предполагается, что читатель уже знаком с лямбда-выражениями..

Основное содержание этой статьи:

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

1. Что такое поток

Поток называется на китайском"поток", путем декларативного преобразования коллекции в такую ​​последовательность элементов, называемую «потоком», для каждого элемента коллекции может быть выполнена серия параллельных или последовательных итераций.Конвейерная обработка.

Другими словами, вам нужно только сказать потоку, что вы хотите, и поток будет обрабатывать элементы в соответствии с требованиями за кулисами, а вам просто нужно «сидеть и наслаждаться».

2. Потоковые операции

Вся потоковая операция представляет собой конвейер, в котором элементы размещаются для обработки один за другим.

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

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

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

Вы думали о чем-то подобном? Да, точно так же, как операторы SQL,select username from user where id = 1, вы просто заявляете: «Мне нужно, чтобы идентификатор был 1 (id = 1)Пользователь(user) имя пользователя (username)», то вы можете получить нужные данные без необходимости самостоятельно обращаться к базе данных для выполнения поиска.

3. Потоки и коллекции

когда рассчитывать

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

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

Поток рассчитывается по запросу, а данные рассчитываются в соответствии с потребностями пользователя.Вы можете себе представить, что мы ищем через поисковую систему.Отображаются не все искомые элементы, а отображаются первые 10 или 20 наиболее подходящих элементов. Только когда вы нажмете «Далее», будут выведены новые 10.

Например, просмотр фильмов онлайн и фильмов на жестком диске похожи.

Внешняя итерация и внутренняя итерация

Еще одно различие между потоками и коллекциями — это итерация.

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

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

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

одноразовый поток

Потоки похожи на итераторы тем, что их можно повторить только один раз.

Stream<String> stream = list.stream().map(Person::getName).sorted().limit(10);         
List<String> newList = stream.collect(toList());
List<String> newList2 = stream.collect(toList());

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

4. Способ введения, начать фактический бой

Сначала мы создаем общий список лиц.

List<Person> list = new ArrayList<>();
list.add(new Person("jack", 20));
list.add(new Person("mike", 25));
list.add(new Person("tom", 30));

Класс Person содержит две переменные-члены: возраст и имя.

private String name;
private int age;

1. stream() / parallelStream()

Наиболее часто используемый метод преобразования коллекции в поток

List list = new ArrayList();
// return Stream<E>
list.stream();

И parallelStream() — это метод параллельного потока, который позволяет набору данных выполнять параллельные операции, которые будут объяснены более подробно позже.

2. filter(T -> boolean)

сохранить элементы, для которых логическое значение истинно

保留年龄为 20 的 person 元素
list = list.stream()
            .filter(person -> person.getAge() == 20)
            .collect(toList());

打印输出 [Person{name='jack', age=20}]

collect(toList()) может преобразовать поток в тип List, что будет объяснено позже.

3. distinct()

Удалить повторяющиеся элементы. Этот метод предназначен для определения равенства двух элементов с помощью метода equals класса.

Например, класс Person в этом примере должен сначала определить метод equals, в противном случае он аналогичен[Person{name='jack', age=20}, Person{name='jack', age=20}]Эта ситуация не будет обработана

4. sorted() / sorted((T, T) -> int)

Если класс элементов в потоке реализует интерфейс Comparable, то есть имеет свои правила сортировки, то для сортировки элементов можно напрямую вызвать метод sorted(), например Stream

Вместо этого вам нужно позвонитьsorted((T, T) -> int)Реализовать интерфейс компаратора

根据年龄大小来比较:
list = list.stream()
           .sorted((p1, p2) -> p1.getAge() - p2.getAge())
           .collect(toList());

Конечно, это можно упростить до

list = list.stream()
           .sorted(Comparator.comparingInt(Person::getAge))
           .collect(toList());

5. limit(long n)

Возвращает первые n элементов

list = list.stream()
            .limit(2)
            .collect(toList());

打印输出 [Person{name='jack', age=20}, Person{name='mike', age=25}]

6. skip(long n)

удалить первые n элементов

list = list.stream()
            .skip(2)
            .collect(toList());

打印输出 [Person{name='tom', age=30}]

tips:

  • При использовании перед limit(n) удалите первые m элементов и верните первые n элементов из оставшихся элементов.
  • Когда limit(n) используется перед skip(m), он возвращает первые n элементов, а затем удаляет m элементов из оставшихся n элементов.
list = list.stream()
            .limit(2)
            .skip(1)
            .collect(toList());

打印输出 [Person{name='mike', age=25}]

7. map(T -> R)

сопоставить каждый элемент T в потоке с R (например, преобразование типа)

List<String> newlist = list.stream().map(Person::getName).collect(toList());

Элементы в новом списке — это переменные имени каждого объекта Person в списке.

8. flatMap(T -> Stream)

Сопоставьте каждый элемент T в потоке с потоком и подключите каждый поток к потоку.

List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");

list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());

В приведенном выше примере наша цель состоит в том, чтобы разделить каждый строковый элемент в списке с помощью " " и превратить его в новый список. Во-первых, метод map разделяет каждый строковый элемент, но в это время тип потока — Stream, поскольку метод split возвращает тип String[]; поэтому нам нужно использовать метод flatMap, сначала используйтеArrays::streamПревратите каждый элемент String[] в поток Stream, затем flatMap объединит каждый поток в поток и, наконец, вернет нужный нам поток.

9. anyMatch(T -> boolean)

соответствует ли элемент в потоке заданномуT -> booleanсостояние

是否存在一个 person 对象的 age 等于 20:
boolean b = list.stream().anyMatch(person -> person.getAge() == 20);

10. allMatch(T -> boolean)

соответствуют ли все элементы в потоке заданномуT -> booleanсостояние

11. noneMatch(T -> boolean)

не соответствует ли ни один элемент в потоке заданномуT -> booleanсостояние

12. найтиЛюбой() и найтиПервый()

  • findAny(): находит один из элементов (первый элемент находится при использовании stream(); один из элементов находится при использовании parallelStream())
  • findFirst(): найти первый элемент

Стоит отметить, что эти два метода возвращают необязательный объект., это класс-контейнер, который может представлять наличие или отсутствие значения, что будет рассмотрено позже.

13. уменьшить ((T, T) -> T) и уменьшить (T, (T, T) -> T)

Используется для объединения элементов в поток, таких как сумма, произведение, максимум и т. д.

计算年龄总和:
int sum = list.stream().map(Person::getAge).reduce(0, (a, b) -> a + b);
与之相同:
int sum = list.stream().map(Person::getAge).reduce(0, Integer::sum);

Среди них первый параметр 0 сокращения представляет собой начальное значение 0, а лямбда(a, b) -> a + bто есть добавление двух значений для получения нового значения

Так же:

计算年龄总乘积:
int sum = list.stream().map(Person::getAge).reduce(1, (a, b) -> a * b);

Конечно вы можете

Optional<Integer> sum = list.stream().map(Person::getAge).reduce(Integer::sum);

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

13. count()

Возвращает количество элементов в потоке в виде длинного

14. collect()

Метод сбора, который мы обычно используем, этоcollect(toList()), и конечноcollect(toSet())Подождите, параметр представляет собой интерфейс коллектора, о котором будет рассказано позже.

15. forEach()

Возвращаемый результат недействителен, и очевидно, что мы можем с ним сделать, например:


### 16. unordered()
还有这个比较不起眼的方法,返回一个等效的无序流,当然如果流本身就是无序的话,那可能就会直接返回其本身

打印各个元素:
list.stream().forEach(System.out::println);

Другим примером является метод сопоставления для доступа к базе данных в MyBatis:

向数据库插入新元素:
list.stream().forEach(PersonMapper::insertPerson);

Связанное Чтение

Вам также может понравиться

Ваше внимание - самая большая мотивация для меня, чтобы продолжать публиковать