Способ ввести функциональное программирование в 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