Guide to Java Streams: forEach() with Examples
The forEach() method is part of the Stream interface and is used to execute a specified operation, defined by a Consumer .
The Consumer interface represents any operation that takes an argument as input, and has no output. This sort of behavior is acceptable because the forEach() method is used to change the program’s state via side-effects, not explicit return types.
Therefore, the best target candidates for Consumers are lambda functions and method references. It’s worth noting that forEach() can be used on any Collection .
forEach() on List
The forEach() method is a terminal operation, which means that after we call this method, the stream along with all of its integrated transformations will be materialized. That is to say, they’ll «gain substance», rather than being streamed.
Let’s generate a small list:
List list = new ArrayList(); list.add(1); list.add(2); list.add(3);
Traditionally, you could write a for-each loop to go through it:
for (Integer element : list) < System.out.print(element + " "); >
Alternatively, we can use the forEach() method on a Stream :
We can make this even simpler via a method reference:
list.stream().forEach(System.out::println);
forEach() on Map
The forEach() method is really useful if we want to avoid chaining many stream methods. Let’s generate a map with a few movies and their respective IMDB scores:
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);
Now, let’s print out the values of each film that has a score higher than 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
Here, we’ve converted a Map to a Set via entrySet() , streamed it, filtered based on the score and finally printed them out via a forEach() . Instead of basing this on the return of filter() , we could’ve based our logic on side-effects and skipped the filter() method:
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
Finally, we can omit both the stream() and filter() methods by starting out with forEach() in the beginning:
map.forEach((k, v) -> < if (v > 8.4) < System.out.println(k + ": " + v); > >);
Free eBook: Git Essentials
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
Forrest Gump: 8.8 The Matrix: 8.7 Who's Singin' Over There?: 8.9
forEach() on Set
Let’s take a look at how we can use the forEach method on a Set in a bit more tangible context. First, let’s define a class that represents an Employee of a company:
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)); > >
Imagining we’re the manager, we’ll want to pick out certain employees that have worked overtime and award them for the hard work. First, let’s make a Set :
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));
Then, let’s calculate each employee’s dedication score:
employees.stream().forEach(Employee::calculateDedication);
Now that each employee has a dedication score, let’s remove the ones with a score that’s too low:
Set regular = employees.stream() .filter(employee -> employee.getDedicationScore() 60) .collect(Collectors.toSet()); employees.removeAll(regular);
Finally, let’s reward the employees for their hard work:
employees.stream().forEach(employee -> employee.receiveReward());
And for clarity’s sake, let’s print out the names of the lucky workers:
System.out.println("Awarded employees:"); employees.stream().map(employee -> employee.getName()).forEach(employee -> System.out.println(employee));
After running the code above, we get the following output:
Vladimir just got a reward for being a dedicated worker! Darinka just got a reward for being a dedicated worker! Awarded employees: Vladimir Darinka
Side-effects Vs Return Values
The point of every command is to evaluate the expression from start to finish. Everything in-between is a side-effect. In this context, it means altering the state, flow or variables without returning any values.
Let’s take a look at the difference on another list:
List targetList = Arrays.asList(1, -2, 3, -4, 5, 6, -7);
This approach is based on the returned values from an ArrayList :
long result1 = targetList .stream() .filter(integer -> integer > 0) .count(); System.out.println("Result: " + result1);
And now, instead of basing the logic of the program on the return type, we’ll perform a forEach() on the stream and add the results to an AtomicInteger (streams operate concurrently):
AtomicInteger result2 = new AtomicInteger(); targetList.stream().forEach(integer -> < if (integer > 0) result2.addAndGet(1); >); System.out.println("Result: " + result2);
Conclusion
The forEach() method is a really useful method to use to iterate over collections in Java in a functional approach.
In certain cases, they can massively simplify the code and enhance clarity and brevity. In this article, we’ve gone over the basics of using a forEach() and then covered examples of the method on a List , Map and Set .
We’ve covered the difference between the for-each loop and the forEach() , as well as the difference between basing logic on return values versus side-effects.
Java stream foreach println
Перебор элементов. Метод forEach
Для перебора элементов потока применяется метод forEach() , который представляет терминальную операцию. В качестве параметра он принимает объект Consumer , который представляет действие, выполняемое для каждого элемента набора. Например:
Stream citiesStream = Stream.of("Париж", "Лондон", "Мадрид","Берлин", "Брюссель"); citiesStream.forEach(s->System.out.println(s));
Фактически это будет аналогично перебору всех элементов в цикле for и выполнению с ними действия, а именно вывод на консоль. В итоге консоль выведет:
Париж Лондон Мадрид Берлин Брюссель
Кстати мы можем сократить в данном случае применение метода forEach следующим образом:
Stream citiesStream = Stream.of("Париж", "Лондон", "Мадрид","Берлин", "Брюссель"); citiesStream.forEach(System.out::println);
Фактически здесь переадается ссылка на статический метод, который выводит строку на консоль.
Фильтрация. Метод filter
Для фильтрации элементов в потоке применяется метод filter() , который представляет промежуточную операцию. Он принимает в качестве параметра некоторое условие в виде объекта Predicate и возвращает новый поток из элементов, которые удовлетворяют этому условию:
Stream citiesStream = Stream.of("Париж", "Лондон", "Мадрид","Берлин", "Брюссель"); citiesStream.filter(s->s.length()==6).forEach(s->System.out.println(s));
Здесь условие s.length()==6 возвращает true для тех элементов, длина которых равна 6 символам. То есть в итоге программа выведет:
Рассмотрим еще один пример фильтрации с более сложными данными. Допустим, у нас есть следующий класс Phone:
class Phone < private String name; private int price; public Phone(String name, int price)< this.name=name; this.price=price; >public String getName() < return name; >public void setName(String name) < this.name = name; >public int getPrice() < return price; >public void setPrice(int price) < this.price = price; >>
Отфильтруем набор телефонов по цене:
Stream phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000), new Phone("Samsung Galaxy S 6", 40000)); phoneStream.filter(p->p.getPrice()<50000).forEach(p->System.out.println(p.getName()));
Отображение. Метод map
Отображение или маппинг позволяет задать функцию преобразования одного объекта в другой, то есть получить из элемента одного типа элемент другого типа. Для отображения используется метод map , который имеет следующее определение:
Stream map(Function mapper)
Передаваемая в метод map функция задает преобразование от объектов типа T к типу R. И в результате возвращается новый поток с преобразованными объектами.
Возьмем вышеопределенный класс телефонов и выполним преобразование от типа Phone к типу String:
Stream phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000), new Phone("Samsung Galaxy S 6", 40000)); phoneStream .map(p-> p.getName()) // помещаем в поток только названия телефонов .forEach(s->System.out.println(s));
Операция map(p-> p.getName()) помещает в новый поток только названия телефонов. В итоге на консоли будут только названия:
iPhone 6 S Lumia 950 Samsung Galaxy S 6
Еще проведем преобразования:
phoneStream .map(p-> "название: " + p.getName() + " цена: " + p.getPrice()) .forEach(s->System.out.println(s));
Здесь также результирующий поток содержит только строки, только теперь названия соединяются с ценами.
Для преобразования объектов в типы Integer, Long, Double определены специальные методы mapToInt() , mapToLong() и mapToDouble() соответственно.
Плоское отображение. Метод flatMap
Плоское отображение выполняется тогда, когда из одного элемента нужно получить несколько. Данную операцию выполняет метод flatMap :
Stream flatMap(Function> mapper)
Например, в примере выше мы выводим название телефона и его цену. Но что, если мы хотим установить для каждого телефона цену со скидкой и цену без скидки. То есть из одного объекта Phone нам надо получить два объекта с информацией, например, в виде строки. Для этого применим flatMap:
Stream phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000), new Phone("Samsung Galaxy S 6", 40000)); phoneStream .flatMap(p->Stream.of( String.format("название: %s цена без скидки: %d", p.getName(), p.getPrice()), String.format("название: %s цена со скидкой: %d", p.getName(), p.getPrice() - (int)(p.getPrice()*0.1)) )) .forEach(s->System.out.println(s));
Результат работы программы:
название: iPhone 6 S цена без скидки: 54000 название: iPhone 6 S цена со скидкой: 48600 название: Lumia 950 цена без скидки: 45000 название: Lumia 950 цена со скидкой: 40500 название: Samsung Galaxy S 6 цена без скидки: 40000 название: Samsung Galaxy S 6 цена со скидкой: 36000