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

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

Вступление

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

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

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

forEach () в списке

Метод forEach() - это терминальная операция, что означает, что после вызова этого метода поток вместе со всеми его интегрированными преобразованиями будет материализован. Другими словами, они «обретут сущность», а не будут транслироваться.

Сформируем небольшой список:

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

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

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

Это напечатает:

 1 2 3 

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

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

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

 1 2 3 

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

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

forEach () на карте

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

 Map<String, Double> map = new HashMap<String, Double>(); 
 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 

Здесь мы преобразовали Map в Set помощью 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() , начав с forEach() в начале:

 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 () в наборе

Давайте посмотрим, как мы можем использовать метод forEach Set в более осязаемом контексте. Во-первых, давайте определим класс, представляющий Employee компании:

 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 :

 Set<Employee> employees = new HashSet<Employee>(); 
 
 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<Employee> 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<Integer> 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() а затем рассмотрели примеры этого метода для List , Map и Set .

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

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus