Способ ввести функциональное программирование в java
Есть два разных способа сделать одно и то же в java.
Императив
декларативный
Хорошо, давайте возьмем пример. Допустим, у нас есть список сотрудников с их зарплатами. Класс сотрудников выглядит следующим образом;
public class Employee { private final String name; private final int year_of_experience; private final long salary; public Employee(String name, int year_of_experience, long salary) { this.name = name; this.year_of_experience = year_of_experience; this.salary = salary; } public String getName() { return name; } public int getYear_of_experience() { return year_of_experience; } public long getSalary() { return salary; } @Override public String toString(){ return "Employee: "+ "name:- " + name+ " years_of_experience:- "+ year_of_experience+ " salary:- "+ salary; } }
И у нас есть список сотрудников.
private static List<Employee> getEmployee() { return List.of( new Employee("Alice", 3, 500_000), new Employee("Bob", 2, 300_000), new Employee("Cindy", 1, 80_000), new Employee("Diana", 8, 700_000), new Employee("Emmy", 5, 500_000), new Employee("Finn", 0, 50_000) ); }
И мы хотим получить всех тех сотрудников, у которых зарплата превышает 100 тысяч.
Императивный подход:
// Imperative approach List<Employee> high_paid_employees = new ArrayList<>(); for(Employee employee: employees){ if (employee.getSalary() > 100_000){ high_paid_employees.add(employee); } } high_paid_employees.forEach(System.out::println);
Декларативный подход:
// Declarative approach List<Employee> high_paid_employees = employees.stream() .filter(e-> e.getSalary() > 100_000) .collect(Collectors.toList()); high_paid_employees.forEach(System.out::println);
Вместо того, чтобы определять каждый шаг, с помощью потоков мы легко смогли отфильтровать необходимые данные и собрать их в отдельный список.
Существует два типа операций, объединенных для формирования потокового конвейера.
Промежуточные операции, в том числе; фильтр, карта, сортировка.
Эти операции всегда ленивы, как всегда.
Операции создают новый поток.
Терминальные операции в том числе; forEach, уменьшить.
Терминальные операции дают результат.
После этих операций поток считается израсходованным и больше не может использоваться.
Потоки над коллекциями:
- Нет хранилища
Сам по себе поток не является структурой данных, поэтому не хранит элементы; вместо этого он действует как конвейер для передачи элементов из источника для выполнения различных операций.
- Функциональный характер
Как указано выше, потоки создают результат и не изменяют источник. Затем мы можем собрать полученный поток в новый список, используя функцию .collect.
- Поиск лени
Многие потоковые операции, такие как фильтрация, сопоставление или удаление дубликатов, могут быть реализованы лениво, открывая возможности для оптимизации. Например, «найти первую строку с тремя последовательными гласными» не нужно проверять все входные строки.
- Возможно, без ограничений
В то время как коллекции имеют конечный размер, потоки в этом не нуждаются. Сокращающие операции, такие как limit(n) или findFirst(), могут позволить выполнять вычисления на бесконечных потоках за конечное время.
- Расходные материалы
Элементы потока посещаются только один раз в течение жизни потока. Как и в случае с java.util.Iterator, для повторного посещения тех же элементов исходного кода необходимо создать новый поток.
Другие операции с потоками включают в себя сортировку, совпадение всех, любое совпадение, отсутствие соответствия, минимум, максимум. Вы должны практиковать их :)
Рекомендации
Учебник по потокам Java, amigoscode: https://www.youtube.com/watch?v=Q93JsQ8vcwY&ab_channel=Amigoscode
Документация по Java: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html