Эта статья была авторизована для эксклюзивной публикации «Выбор серверной технологии».
Потоки позволяют программистам работать с коллекциями на уровне абстракции.
От внешней итерации к внутренней итерации
Что такое внешняя итерация и внутренняя итерация?
Лично внешнее и внутреннее относятся к коду коллекции.
Если итеративный бизнес выполняется в коде приложения, он называется внешней итерацией.
И наоборот, итерация выполняется в коде коллекции, что называется внутренней итерацией (функциональное программирование).
Описание языка может быть немного абстрактным, см. примеры ниже.
1. Внешняя итерация
Вызовите метод итератора, чтобы создать новый объект Iterator, а затем управляйте всем процессом итерации.
for (Student student:list){
if (student.getAge()>18){
result++;
}
}
Мы все знаем, что for фактически использует итератор внизу:
Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()){
Student student = iterator.next();
if (student.getAge()>18){
result++;
}
}
Приведенный выше метод итерации является внешней итерацией.
Недостатки внешней итерации:
- Сложность абстрагирования сложных операций
- По сути сериализованная операция.
2. Внутренняя итерация
Возвращает интерфейс ответа во внутренней итерации: Stream
long count = list.stream().filter(student -> student.getAge() > 18).count();
Весь процесс разбит на: фильтрацию и подсчет.
обращать внимание: Возвращаемый объект Stream — это не новая коллекция, а рецепт создания новой коллекции.
2.1 Ленивая оценка и ранняя оценка
Метод, который описывает поток по значению, подобному фильтру, и в конечном итоге не создает новую коллекцию, называетсяленивая оценка. Такой метод, как count, который в конечном итоге даст значение из Stream, называетсяРанняя оценка.
Чтобы определить, является ли операция ленивой или оценивается рано, вам нужно только посмотреть на ее возвращаемое значение. Если возвращаемое значение является потоком, это отложенная оценка; если возвращаемое значение является другим значением или пусто, это ранняя оценка. Идеальный способ для этих операций — сформировать цепочку с ленивой оценкой, заканчивающуюся операцией ранней оценки, которая возвращает желаемый результат.
Весь процесс очень похож на паттерн построителя: после выполнения ряда операций вызывается метод сборки для возврата нужного объекта.Шаблон проектирования быстрого обучения (4) шаблон строителя
каков процессвыгодаЧто: Вы можете каскадировать несколько операций над классом коллекции, но итерация должна произойти только один раз.
3. Общие операции
3.1 собирать(toList()) раннюю оценку
Метод collect(toList()) генерирует список из значений в Stream и является операцией ранней оценки.
List<String> collect = Stream.of("a", "b", "c").collect(Collectors.toList());
Stream.of("a", "b", "c")
Сначала сгенерируйте объект Stream из списка, затемcollect(Collectors.toList())
Создание объектов списка.
3.2 map
map может преобразовывать значение одного типа в другой.
List<String> streamMap = Stream.of("a", "b", "c").map(String -> String.toUpperCase()).collect(Collectors.toList());
map(String -> String.toUpperCase())
вернет объект Stream всех заглавных букв алфавита,collect(Collectors.toList())
Список возврата.
3.3 фильтр фильтр
При обходе и проверке элементов в нем вы можете использовать фильтр
List<String> collect1 = Stream.of("a", "ab", "abc")
.filter(value -> value.contains("b"))
.collect(Collectors.toList());
3.4 flatMap
Если есть объект, содержащий несколько коллекций, и вы хотите получить коллекцию всех чисел, мы можем использовать flatMap
List<Integer> collect2 = Stream.of(asList(1, 2), asList(3, 4))
.flatMap(Collection::stream)
.collect(Collectors.toList());
Stream.of(asList(1, 2), asList(3, 4))
Преобразуйте каждую коллекцию в объект Stream, затем.flatMap
Обработан в новый объект Stream.
3,5 макс и мин
Просто посмотрите на название, максимальное и минимальное значения.
Student student1 = list.stream()
.min(Comparator.comparing(student -> student.getAge()))
.get();
java8 предоставляетComparator
Статический метод, с помощью которого вы можете реализовать удобный компаратор. вComparator.comparing(student -> student.getAge()
можно заменить наComparator.comparing(Student::getAge)
становится более чистой лямбдой.max
То же самое справедливо.
3.6 reduce
Операция сокращения может быть реализована для создания значения из набора значений.Методы count, min и max, используемые в приведенных выше примерах, на самом деле являются операциями сокращения.
Integer reduce = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
System.out.println(reduce);
6
В приведенном выше примере используется сведение к сумме, 0 представляет собой начальную точку, acc представляет собой аккумулятор, который содержит текущий результат накопления (элементы в потоке накапливаются в соответствии на каждом шаге), а element является текущим элементом.
4. Операционная интеграция
- Метод collect(toList()) генерирует список из значений в Stream
- map может преобразовывать значение одного типа в другой.
- При обходе и проверке элементов в нем вы можете использовать фильтр
- Если есть объект, содержащий несколько коллекций, и вы хотите получить коллекцию всех чисел, мы можем использовать flatMap
- макс и мин
- уменьшить (обычно не используется)
5. Цепная операция фактический бой
List<Student> students = new ArrayList<>();
students.add(new Student("Fant.J",18));
students.add(new Student("小明",19));
students.add(new Student("小王",20));
students.add(new Student("小李",22));
List<Class> classList = new ArrayList<>();
classList.add(new Class(students,"1601"));
classList.add(new Class(students,"1602"));
static class Student{
private String name;
private Integer age;
getter and setter ...and construct ....
}
static class Class{
private List<Student> students;
private String className;
getter and setter ...and construct ....
}
Это наши данные и отношения — классы и ученики, теперь я хочу найти учеников, имена которых начинаются с маленькой, и цепочки операций с потоками:
List<String> list= students.stream()
.filter(student -> student.getAge() > 18)
.map(Student::getName)
.collect(Collectors.toList());
[小明, 小王, 小李]
Это простая связанная реализация объекта «Поток студентов». Что, если я хочу найти объекты с возрастом больше 18 лет во многих классах?
6. Улучшение реального боя
Найдите имена старше 18 лет во многих классах и верните набор.
Оригинальный код:
List<String> nameList = new ArrayList<>();
for (Class c:classList){
for (Student student:c.getStudents()){
if (student.getAge()>18){
String name = student.getName();
nameList.add(name);
}
}
}
System.out.println(nameList);
Код связанного потока:
Если бы вас попросили написать, вы могли быclassList.stream().forEach(aClass -> aClass.getStudents().stream())....
достигать?
Я был такой безмозглый в начале, потом опомнился и вспомнил, что foreach это ранняя операция оценки, а возвращаемое значение void, поэтому начало обречено на отсутствие результатов, а потом думаю хорошенько, flatMap не используется для обработки потока, не являющегося коллекцией, ну и есть следующий код.
List<String> collect = classList.stream()
.flatMap(aclass -> aclass.getStudents().stream())
.filter(student -> student.getAge() > 18)
.map(Student::getName)
.collect(toList());
По сравнению с вызовом цепочки потоков исходный код имеет следующие недостатки.:
- Плохая читабельность кода скрывает реальную бизнес-логику
- Необходимо установить посторонние переменные для хранения промежуточных результатов
- Неэффективно, каждый шаг оценивается заранее для создания нового набора
- Сложно распараллелить
7. Функции высшего порядка и меры предосторожности
Функция высшего порядка — это функция, которая принимает другую функцию в качестве параметра или возвращает функцию. Функция является функцией высшего порядка, если ее функция содержит интерфейс или возвращает интерфейс.
Почти все функции в интерфейсе Stream являются функциями высшего порядка. Например: Comparing принимает функцию в качестве параметра и возвращает интерфейс Comparator.
Student student = list.stream().max(Comparator.comparing(Student::getAge)).get();
public interface Comparator<T> {}
Метод foreach также является функцией более высокого порядка:
void forEach(Consumer<? super T> action);
public interface Consumer<T> {}
В дополнение к вышеперечисленным функциям более высокого порядка с похожими функциями не рекомендуется передавать лямбда-выражения в функции более высокого порядка в Stream, потому что большинство функций более высокого порядка будут иметь некоторые побочные эффекты: присваивание переменных переменным. Локальные переменные присваивают значения переменным-членам, что затрудняет их обнаружение.
Вот пример ActionEvent: