Рубрики
Без рубрики

Руководство по потокам Java: forEach() с примерами

В этом уроке мы рассмотрим метод Java Streams forEach (). Мы рассмотрим основные способы использования и примеры forEach() в списке, карте и наборе.

Автор оригинала: Vladimir Batoćanin.

Вступление

Метод forEach() является частью интерфейса Stream и используется для выполнения указанной операции, определенной Потребителем .

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

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

forEach() в списке

Метод forEach() является терминальной операцией, что означает, что после вызова этого метода поток вместе со всеми его интегрированными преобразованиями будет материализован. То есть они “обретут сущность”, а не будут транслироваться.

Давайте сгенерируем небольшой список:

List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);

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

for (Integer element : list) {
    System.out.print(element + " ");
}

Это будет печатать:

1 2 3

В качестве альтернативы мы можем использовать метод forEach() для потока :

list.stream().forEach((k) -> {
    System.out.print(k + " ");
});

Это также выводит:

1 2 3

Мы можем сделать это еще проще с помощью ссылки на метод:

list.stream().forEach(System.out::println);

forEach() на карте

Метод forEach() действительно полезен, если мы хотим избежать цепочки многих потоковых методов. Давайте сгенерируем карту с несколькими фильмами и соответствующими оценками IMDB:

Map map = new HashMap();
map.put("Forrest Gump", 8.8);
map.put("The Matrix", 8.7);
map.put("The Hunt", 8.3);
map.put("Monty Python's Life of Brian", 8.1);
map.put("Who's Singin' Over There?", 8.9);

Теперь давайте распечатаем значения каждого фильма, у которого оценка выше, чем 8.4 :

map.entrySet()
        .stream()
        .filter(entry -> entry.getValue() > 8.4)
        .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));

Это приводит к:

Forrest Gump: 8.8
The Matrix: 8.7
Who's Singin' Over There?: 8.9

Здесь мы преобразовали Карту в Набор через entrySet () , передали ее в потоковом режиме , отфильтровали на основе результатов и, наконец, распечатали их с помощью forEach() . Вместо того , чтобы основывать это на возврате filter () , мы могли бы основать нашу логику на побочных эффектах и пропустить метод filter() :

map.entrySet()
        .stream()
        .forEach(entry -> {
            if (entry.getValue() > 8.4) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
                }
            }
        );

Это приводит к:

Forrest Gump: 8.8
The Matrix: 8.7
Who's Singin' Over There?: 8.9

Наконец, мы можем опустить оба метода stream() и filter () , начав с для каждого() в начале:

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

map.forEach((k, v) -> {
        if (v > 8.4) {
            System.out.println(k + ": " + v);
         }
     });

Это приводит к:

Forrest Gump: 8.8
The Matrix: 8.7
Who's Singin' Over There?: 8.9

для Каждого() наступления

Давайте посмотрим, как мы можем использовать метод forEach для Набора в немного более осязаемом контексте. Во-первых, давайте определим класс, который представляет Сотрудника компании:

public class Employee {
    private String name;
    private double workedHours;
    private double dedicationScore;
    
    // Constructor, Getters and Setters
    
    public void calculateDedication() {
        dedicationScore = workedHours*1.5;
    }
    
    public void receiveReward() {
        System.out.println(String
            .format("%s just got a reward for being a dedicated worker!", name));
    }
}

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

 Set employees = new HashSet();

 employees.add(new Employee("Vladimir", 60));
 employees.add(new Employee("John", 25));
 employees.add(new Employee("David", 40));
 employees.add(new Employee("Darinka", 60));

Затем давайте рассчитаем показатель преданности каждого сотрудника:

employees.stream().forEach(Employee::calculateDedication);

Теперь, когда у каждого сотрудника есть оценка самоотдачи, давайте удалим тех, у кого слишком низкий балл:

Set regular = employees.stream()
        .filter(employee -> employee.getDedicationScore() <= 60)
        .collect(Collectors.toSet());
employees.removeAll(regular);

Наконец, давайте вознаградим сотрудников за их усердную работу:

employees.stream().forEach(employee -> employee.receiveReward());

И для ясности давайте распечатаем имена счастливчиков:

System.out.println("Awarded employees:");
employees.stream().map(employee -> employee.getName()).forEach(employee -> System.out.println(employee));

После выполнения приведенного выше кода мы получим следующий вывод:

Vladimir just got a reward for being a dedicated worker!
Darinka just got a reward for being a dedicated worker!
Awarded employees:
Vladimir
Darinka

Побочные эффекты И Возвращаемые Значения

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

Давайте взглянем на разницу в другом списке:

List targetList = Arrays.asList(1, -2, 3, -4, 5, 6, -7);

Этот подход основан на возвращаемых значениях из ArrayList :

long result1 = targetList
        .stream()
        .filter(integer -> integer > 0)
        .count();
System.out.println("Result: " + result1);

Это приводит к:

Result: 4

И теперь, вместо того, чтобы основывать логику программы на типе возвращаемого значения, мы выполним forEach() в потоке и добавим результаты в AtomicInteger (потоки работают одновременно):

AtomicInteger result2 = new AtomicInteger();

targetList.stream().forEach(integer -> {
            if (integer > 0)
                result2.addAndGet(1);
            });
System.out.println("Result: " + result2);

Вывод

Метод forEach () – действительно полезный метод, который можно использовать для перебора коллекций в Java в функциональном подходе.

В некоторых случаях они могут значительно упростить код и повысить ясность и краткость. В этой статье мы рассмотрели основы использования forEach () , а затем рассмотрели примеры метода в Списке , Карте и Наборе .

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