The better practice is to use for-each. Besides violating the Keep It Simple, Stupid principle, the new-fangled forEach() has at least the following deficiencies:

  • Can't use non-final variables. So, code like the following can't be turned into a forEach lambda:
Object prev = null;
for(Object curr : list)
{
    if( prev != null )
        foo(prev, curr);
    prev = curr;
}
  • Can't handle checked exceptions. Lambdas aren't actually forbidden from throwing checked exceptions, but common functional interfaces like Consumer don't declare any. Therefore, any code that throws checked exceptions must wrap them in try-catch or Throwables.propagate(). But even if you do that, it's not always clear what happens to the thrown exception. It could get swallowed somewhere in the guts of forEach()

  • Limited flow-control. A return in a lambda equals a continue in a for-each, but there is no equivalent to a break. It's also difficult to do things like return values, short circuit, or set flags (which would have alleviated things a bit, if it wasn't a violation of the no non-final variables rule). "This is not just an optimization, but critical when you consider that some sequences (like reading the lines in a file) may have side-effects, or you may have an infinite sequence."

  • Might execute in parallel, which is a horrible, horrible thing for all but the 0.1% of your code that needs to be optimized. Any parallel code has to be thought through (even if it doesn't use locks, volatiles, and other particularly nasty aspects of traditional multi-threaded execution). Any bug will be tough to find.

  • Might hurt performance, because the JIT can't optimize forEach()+lambda to the same extent as plain loops, especially now that lambdas are new. By "optimization" I do not mean the overhead of calling lambdas (which is small), but to the sophisticated analysis and transformation that the modern JIT compiler performs on running code.

  • If you do need parallelism, it is probably much faster and not much more difficult to use an ExecutorService. Streams are both automagical (read: don't know much about your problem) and use a specialized (read: inefficient for the general case) parallelization strategy (fork-join recursive decomposition).

  • Makes debugging more confusing, because of the nested call hierarchy and, god forbid, parallel execution. The debugger may have issues displaying variables from the surrounding code, and things like step-through may not work as expected.

  • Streams in general are more difficult to code, read, and debug. Actually, this is true of complex "fluent" APIs in general. The combination of complex single statements, heavy use of generics, and lack of intermediate variables conspire to produce confusing error messages and frustrate debugging. Instead of "this method doesn't have an overload for type X" you get an error message closer to "somewhere you messed up the types, but we don't know where or how." Similarly, you can't step through and examine things in a debugger as easily as when the code is broken into multiple statements, and intermediate values are saved to variables. Finally, reading the code and understanding the types and behavior at each stage of execution may be non-trivial.

  • Sticks out like a sore thumb. The Java language already has the for-each statement. Why replace it with a function call? Why encourage hiding side-effects somewhere in expressions? Why encourage unwieldy one-liners? Mixing regular for-each and new forEach willy-nilly is bad style. Code should speak in idioms (patterns that are quick to comprehend due to their repetition), and the fewer idioms are used the clearer the code is and less time is spent deciding which idiom to use (a big time-drain for perfectionists like myself!).

As you can see, I'm not a big fan of the forEach() except in cases when it makes sense.

Particularly offensive to me is the fact that Stream does not implement Iterable (despite actually having method iterator) and cannot be used in a for-each, only with a forEach(). I recommend casting Streams into Iterables with (Iterable<T>)stream::iterator. A better alternative is to use StreamEx which fixes a number of Stream API problems, including implementing Iterable.

That said, forEach() is useful for the following:

  • Atomically iterating over a synchronized list. Prior to this, a list generated with Collections.synchronizedList() was atomic with respect to things like get or set, but was not thread-safe when iterating.

  • Parallel execution (using an appropriate parallel stream). This saves you a few lines of code vs using an ExecutorService, if your problem matches the performance assumptions built into Streams and Spliterators.

  • Specific containers which, like the synchronized list, benefit from being in control of iteration (although this is largely theoretical unless people can bring up more examples)

  • Calling a single function more cleanly by using forEach() and a method reference argument (ie, list.forEach (obj::someMethod)). However, keep in mind the points on checked exceptions, more difficult debugging, and reducing the number of idioms you use when writing code.

Articles I used for reference:

  • Everything about Java 8
  • Iteration Inside and Out (as pointed out by another poster)

EDIT: Looks like some of the original proposals for lambdas (such as http://www.javac.info/closures-v06a.html Google Cache) solved some of the issues I mentioned (while adding their own complications, of course).

Answer from Aleksandr Dubinsky on Stack Overflow
🌐
Baeldung
baeldung.com › home › java › core java › guide to the java foreach loop
Guide to the Java forEach Loop | Baeldung
June 17, 2025 - Introduced in Java 8, the forEach() method provides programmers with a concise way to iterate over a collection.
🌐
Reddit
reddit.com › r/javahelp › lambda vs for each loops
r/javahelp on Reddit: Lambda vs For each loops
December 30, 2018 -

I've been reading on lambdas on Java and I'm wondering what is better to use in code or if there is no difference. I'm used to writing arrow functions in JavaScript and it seems like these two functions are equivalent.

Example:

for (String str : arrayOfStrings) {

System.out.println(str);

}

// vs

arrayOfStrings.forEach((str) ->{

System.out.println(str);

} )

Discussions

Why does the List interface have forEach, but not map?
why isn't every List a Stream? To answer this part of the question, it's because a stream is a fundamentally different datatype than a list. A list represents a collection of data, while a stream represents a pipeline for processing data. Streams can only be processed once, and are designed to perform multiple operations on some provided data. They don't store any intermediate results, and then do lazy processing once you specify a terminal operator (like toList() or forEach()). For example, if you had something like this: Stream a = List.of(1, 2, 3).stream(); Stream b = a.filter(n -> n > 1); Stream c = b.map(n -> String.valueOf(n)); List d = c.toList(); This is functionally identical to this: List d = List.of(1, 2, 3).stream() .filter(n -> n > 1) .map(n -> String.valueOf(n)) .toList(); If you inspected b, however, it would not contain any data about what elements were remaining, because the stream has not executed yet. Assigning the various steps of the stream along the way doesn't actually DO anything to the data, it just creates pointers to instances of the pipeline. As mentioned before, the pipeline can only be executed once, which means a stream is consumed when you perform a terminal operation on it. It also can't be executed if you chain it to another pipeline. If, in the example above, you tried to do b.toList(), you'd get an IllegalStateException, because b was already operated on. If you did c.toList(), you'd get a list of "2", "3". If you did c.toList() a second time, you'd once again get an IllegalStateExcecution, because c has already been consumed. The intermediate operators like map actually do nothing except modify the pipeline definition until you call toList at the end. Once you execute toList, then the pipeline operates in a lazy way, evaluating the data elements one at a time. So, to recap: List: collection of ordered data Stream: an operation pipeline definition More on reddit.com
🌐 r/java
90
121
October 26, 2024
What is the difference between .forEach() and .forEach{}
In Kotlin, if a function consumes a function type as the last argument, then any lambda passed as that parameter can be specified outside the parenthesis. It is just syntatic sugar to make it look nicer, and is inspired by how other languages do this like Groovy, Ruby, etc. E.g. let's say you defined this function that calls a function type for every other item in a list of ints: fun forEveryOther(integers: List, consumer: (Int) -> Unit) { var index = 0 while (index < integers.size) { consumer(integers[index]) index += 2 } } Then you could call it like this: val integers = listOf(9, 18, 27, 36, 45, 54, 63, 72, 81) forEveryOther(integers, { integer -> println(integer) }) Or you could put the Lambda outside the function parenthesis, which means the same thing but makes multiline lambdas a bit less messy val integers = listOf(9, 18, 27, 36, 45, 54, 63, 72, 81) forEveryOther(integers) { integer -> println(integer) } This doesn't work for anything that isn't a lambda literal though. E.g. function references have to still be inside the parenthesis. val integers = listOf(9, 18, 27, 36, 45, 54, 63, 72, 81) forEveryOther(integers, printer::printInt) More on reddit.com
🌐 r/Kotlin
6
28
July 23, 2024
What is the difference between map and forEach in Java streams in this case?
Please ensure that: Your code is properly formatted as code block - see the sidebar (About on mobile) for instructions You include any and all error messages in full - best also formatted as code block You ask clear questions You demonstrate effort in solving your question/problem - plain posting your assignments is forbidden (and such posts will be removed) as is asking for or giving solutions. If any of the above points is not met, your post can and will be removed without further warning. Code is to be formatted as code block (old reddit/markdown editor: empty line before the code, each code line indented by 4 spaces, new reddit: https://i.imgur.com/EJ7tqek.png ) or linked via an external code hoster, like pastebin.com, github gist, github, bitbucket, gitlab, etc. Please, do not use triple backticks (```) as they will only render properly on new reddit, not on old reddit. Code blocks look like this: public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } } You do not need to repost unless your post has been removed by a moderator. Just use the edit function of reddit to make sure your post complies with the above. If your post has remained in violation of these rules for a prolonged period of time (at least an hour), a moderator may remove it at their discretion. In this case, they will comment with an explanation on why it has been removed, and you will be required to resubmit the entire post following the proper procedures. To potential helpers Please, do not help if any of the above points are not met, rather report the post. We are trying to improve the quality of posts here. In helping people who can't be bothered to comply with the above points, you are doing the community a disservice. I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns. More on reddit.com
🌐 r/learnjava
12
2
May 11, 2024
Exceptions in Java lambdas
Java streams don’t play well with checked exceptions. That's true, but there might well be something we could do about that. Java’s initial design made plenty of use of checked exceptions. The evolution of programming languages proved that it was not a good idea. That's not true, actually. Haskell, Zig, and Rust also have checked exceptions (they might represent them differently syntactically, but that's a superficial difference). The main difference -- and the cause of the stream incompatibility problem -- is that checked exceptions in Java are a union type, and we don't currently have a way of representing union types with generics, exactly as u/eliasv points out. More on reddit.com
🌐 r/java
40
54
August 26, 2022
Top answer
1 of 8
653

The better practice is to use for-each. Besides violating the Keep It Simple, Stupid principle, the new-fangled forEach() has at least the following deficiencies:

  • Can't use non-final variables. So, code like the following can't be turned into a forEach lambda:
Object prev = null;
for(Object curr : list)
{
    if( prev != null )
        foo(prev, curr);
    prev = curr;
}
  • Can't handle checked exceptions. Lambdas aren't actually forbidden from throwing checked exceptions, but common functional interfaces like Consumer don't declare any. Therefore, any code that throws checked exceptions must wrap them in try-catch or Throwables.propagate(). But even if you do that, it's not always clear what happens to the thrown exception. It could get swallowed somewhere in the guts of forEach()

  • Limited flow-control. A return in a lambda equals a continue in a for-each, but there is no equivalent to a break. It's also difficult to do things like return values, short circuit, or set flags (which would have alleviated things a bit, if it wasn't a violation of the no non-final variables rule). "This is not just an optimization, but critical when you consider that some sequences (like reading the lines in a file) may have side-effects, or you may have an infinite sequence."

  • Might execute in parallel, which is a horrible, horrible thing for all but the 0.1% of your code that needs to be optimized. Any parallel code has to be thought through (even if it doesn't use locks, volatiles, and other particularly nasty aspects of traditional multi-threaded execution). Any bug will be tough to find.

  • Might hurt performance, because the JIT can't optimize forEach()+lambda to the same extent as plain loops, especially now that lambdas are new. By "optimization" I do not mean the overhead of calling lambdas (which is small), but to the sophisticated analysis and transformation that the modern JIT compiler performs on running code.

  • If you do need parallelism, it is probably much faster and not much more difficult to use an ExecutorService. Streams are both automagical (read: don't know much about your problem) and use a specialized (read: inefficient for the general case) parallelization strategy (fork-join recursive decomposition).

  • Makes debugging more confusing, because of the nested call hierarchy and, god forbid, parallel execution. The debugger may have issues displaying variables from the surrounding code, and things like step-through may not work as expected.

  • Streams in general are more difficult to code, read, and debug. Actually, this is true of complex "fluent" APIs in general. The combination of complex single statements, heavy use of generics, and lack of intermediate variables conspire to produce confusing error messages and frustrate debugging. Instead of "this method doesn't have an overload for type X" you get an error message closer to "somewhere you messed up the types, but we don't know where or how." Similarly, you can't step through and examine things in a debugger as easily as when the code is broken into multiple statements, and intermediate values are saved to variables. Finally, reading the code and understanding the types and behavior at each stage of execution may be non-trivial.

  • Sticks out like a sore thumb. The Java language already has the for-each statement. Why replace it with a function call? Why encourage hiding side-effects somewhere in expressions? Why encourage unwieldy one-liners? Mixing regular for-each and new forEach willy-nilly is bad style. Code should speak in idioms (patterns that are quick to comprehend due to their repetition), and the fewer idioms are used the clearer the code is and less time is spent deciding which idiom to use (a big time-drain for perfectionists like myself!).

As you can see, I'm not a big fan of the forEach() except in cases when it makes sense.

Particularly offensive to me is the fact that Stream does not implement Iterable (despite actually having method iterator) and cannot be used in a for-each, only with a forEach(). I recommend casting Streams into Iterables with (Iterable<T>)stream::iterator. A better alternative is to use StreamEx which fixes a number of Stream API problems, including implementing Iterable.

That said, forEach() is useful for the following:

  • Atomically iterating over a synchronized list. Prior to this, a list generated with Collections.synchronizedList() was atomic with respect to things like get or set, but was not thread-safe when iterating.

  • Parallel execution (using an appropriate parallel stream). This saves you a few lines of code vs using an ExecutorService, if your problem matches the performance assumptions built into Streams and Spliterators.

  • Specific containers which, like the synchronized list, benefit from being in control of iteration (although this is largely theoretical unless people can bring up more examples)

  • Calling a single function more cleanly by using forEach() and a method reference argument (ie, list.forEach (obj::someMethod)). However, keep in mind the points on checked exceptions, more difficult debugging, and reducing the number of idioms you use when writing code.

Articles I used for reference:

  • Everything about Java 8
  • Iteration Inside and Out (as pointed out by another poster)

EDIT: Looks like some of the original proposals for lambdas (such as http://www.javac.info/closures-v06a.html Google Cache) solved some of the issues I mentioned (while adding their own complications, of course).

2 of 8
174

The advantage comes into account when the operations can be executed in parallel. (See http://java.dzone.com/articles/devoxx-2012-java-8-lambda-and - the section about internal and external iteration)

  • The main advantage from my point of view is that the implementation of what is to be done within the loop can be defined without having to decide if it will be executed in parallel or sequential

  • If you want your loop to be executed in parallel you could simply write

     joins.parallelStream().forEach(join -> mIrc.join(mSession, join));
    

    You will have to write some extra code for thread handling etc.

Note: For my answer I assumed joins implementing the java.util.Stream interface. If joins implements only the java.util.Iterable interface this is no longer true.

🌐
Oodlestechnologies
oodlestechnologies.com › blogs › java-8-foreach-and-lambda-expression-for-map-and-list
Java 8 forEach and Lambda Expression For Map and List
February 14, 2020 - forEach loop, in Java 8, provides programmers a new, interesting and concise way of iterating over a Collection. Lambda expression is also a new and important feature of Java which was included in Java 8. It provides a concise and clear way to represent one method interface using an expression.
🌐
W3Schools
w3schools.com › java › ref_arraylist_foreach.asp
Java ArrayList forEach() Method
import java.util.ArrayList; public ... action on every item in a list. The action can be defined by a lambda expression that is compatible with the accept() method of Java's Consumer interface....
🌐
Mkyong
mkyong.com › home › java8 › java 8 foreach examples
Java 8 forEach examples - Mkyong.com
December 4, 2020 - 1.2 In Java 8, we can use forEach to loop a Map and print out its entries. public static void loopMapJava8() { Map<String, Integer> map = new HashMap<>(); map.put("A", 10); map.put("B", 20); map.put("C", 30); map.put("D", 40); map.put("E", 50); map.put("F", 60); // lambda map.forEach((k, v) -> System.out.println("Key : " + k + ", Value : " + v)); } Output ·
🌐
Vultr
docs.vultr.com › java › standard-library › java › util › ArrayList › forEach
Java ArrayList forEach() - Apply Action To Each Item | Vultr Docs
September 27, 2024 - List<String> items = Arrays.asList("Apple", "Banana", "Cherry"); items.forEach(item -> System.out.println(item)); Explain Code · This example prints each string element in the items list. The lambda expression item -> System.out.println(item) is executed for each element.
Find elsewhere
🌐
Tech with Maddy
techwithmaddy.com › how-does-the-foreach-loop-work-in-java
How does the "forEach" loop work in Java?
October 24, 2021 - Let's see examples to understand how the forEach() works on Collections and Streams. import java.util.ArrayList; import java.util.List; public class Fruit { public static void main(String[] args) { List<String> listOfFruits = new ArrayList<>(); listOfFruits.add("apple"); listOfFruits.add("pear"); listOfFruits.add("banana"); listOfFruits.add("mango"); //using a lambda expression listOfFruits.forEach(x -> System.out.println(x)); //using a method reference listOfFruits.forEach(System.out::println); } }
🌐
Java67
java67.com › 2016 › 01 › how-to-use-foreach-method-in-java-8-examples.html
10 Examples of forEach() method in Java 8 | Java67
how about the calculating sum of the length of all strings? you can do so by using fold operations like sum() as shown in the following example: alphabets.stream() .mapToInt(s -> s.length()) .sum(); These were some of the common but very useful examples of Java 8's forEach() method, a new way to loop over List in Java. If you are feeling nostalgist then don't forget to the journey of for loop in Java, a recap of for loop from JDK 1 to JDK 8 If you want to learn more about functional programming in Java 8 and using a map, flatmap methods then I suggest you go through Learn Java Functional Programming with Lambdas & Streams course on Udemy.
🌐
ZetCode
zetcode.com › java › foreach
Java forEach - forEach on Java lists, maps, sets
June 17, 2025 - Each consumer is defined with a lambda expression to print the elements with a space separator, showcasing their application to primitive streams. The forEach method can be combined with other stream operations, such as mapping and filtering, to perform complex transformations before processing ...
🌐
GeeksforGeeks
geeksforgeeks.org › java › foreach-loop-vs-stream-foreach-vs-parallel-stream-foreach
foreach() loop vs Stream foreach() vs Parallel Stream foreach() - GeeksforGeeks
July 12, 2025 - ... public class GFG { public static ... ... Lambda operator is used: In stream().forEach(), lambdas are used and thus operations on variables outside the loop are not allowed....
🌐
Javatpoint
javatpoint.com › java-8-foreach
Java 8 forEach() Method
Java 8 forEach Tutorial with examples and topics on functional interface, anonymous class, lambda for list, lambda for comparable, default methods, method reference, java date and time, java nashorn, java optional, stream, filter etc.
🌐
HowToDoInJava
howtodoinjava.com › home › java 8 › java foreach()
Java forEach() with Examples - HowToDoInJava
February 6, 2023 - In this Java 8 tutorial, we learned to use the forEach() method for iterating though the items in Java Collections and/or Streams. We leaned to perform Consumer and BiConsumer action in form of annonymous methods as well as lambda expressions.
🌐
Roy Tutorials
roytuts.com › home › java › java foreach example using lambda expression
Java forEach Example Using Lambda Expression - Roy Tutorials
December 2, 2021 - Create the below class to iterate List and Map using forEach() loop. ... public class LambdaForEachExample { public static void main(String[] args) { List<Integer> integers = new ArrayList<>(); integers.add(1); integers.add(2); integers.add(3); integers.add(5); integers.add(7); integers.add(11); System.out.println("Iterating over List"); System.out.println("-------------------"); System.out.println(); System.out.println("Accessing elements without using stream API"); System.out.println("Print elements using method reference:"); integers.forEach(System.out::println); System.out.println("Printin
🌐
Coderanch
coderanch.com › t › 777785 › java › forEach-java-util-function-Consumer
'forEach(java.util.function.Consumer)' in 'java.lang.Iterable' cannot be applied (Features new in Java 8 forum at Coderanch)
I think you guys are missing Monica's point. The goal is to NOT use a lambda expression, but instead replace it completely with an instance of an anonymous class. Monica, forEach() requires a Consumer. You can't pass it a MyInterface, because MyInterface isn't a Consumer.
🌐
Medium
medium.com › @AlexanderObregon › javas-map-foreach-method-explained-0fe0dc60fa5e
Java’s Map.forEach() Method Explained | Medium
December 7, 2024 - import java.util.HashMap; import java.util.Map; public class LoggingExample { public static void main(String[] args) { Map<String, Integer> data = new HashMap<>(); data.put("User1", 200); data.put("User2", 300); data.put("User3", 150); data.forEach((user, score) -> System.out.println(user + " scored: " + score)); } } ... This example highlights the simplicity of using a lambda expression with forEach() to process and log data.
🌐
Studyeasy
studyeasy.org › course-articles › java-en-en › s13l06-foreach-loop-for-lambda-expression
S13L06 – ForEach loop for Lambda expression – Studyeasy
February 13, 2025 - Introduce the concept of ForEach loops in Java. Explore the integration of Lambda expressions to enhance iteration.
🌐
Medium
neesri.medium.com › exploring-the-foreach-method-in-java-8-stream-api-3a871cdc07a0
Exploring the forEach Method in Java 8 Stream API | by A cup of JAVA coffee with NeeSri | Medium
July 20, 2025 - The forEach method is a terminal ... of elements in the stream. It takes a single argument—a lambda expression or a method reference—representing the action to be performed on each element....
🌐
GeeksforGeeks
geeksforgeeks.org › java › lambda-expressions-java-8
Java Lambda Expressions - GeeksforGeeks
Lambda expressions are widely used with Java Collections and Streams for concise operations ... import java.util.Arrays; import java.util.List; public class GFG{ public static void main(String[] args){ List<String> names = Arrays.asList( "Alice", "Bob", "Charlie", "Adam"); System.out.println("All names:"); names.forEach(name -> System.out.println(name)); System.out.println("\nNames starting with 'A':"); names.stream() .filter(n -> n.startsWith("A")) .map(n -> n.toUpperCase()) .forEach(System.out::println); } }
Published   3 days ago
🌐
Medium
raphaeldelio.medium.com › why-wont-my-foreach-lambda-allow-me-to-exit-my-function-with-a-return-statement-986792e9c054
Why won't my forEach lambda allow me to exit my function with a return statement in Java? | by Raphael De Lio | Medium
January 25, 2024 - Just like any other function, a lambda can receive arguments and also expect something to be returned. When you try to return false from the forEach lambda, you’re actually trying to exit this anonymous function and output a boolean. However, the forEach function is not expected to return any result.