WARNING
As mentioned in comments, Using peek() for production code is considered bad practice
The reasson is that "According to its JavaDocs, the intermediate Stream operation java.util.Stream.peek() “exists mainly to support debugging” purposes."
As a consequence, this proposed solution SHOULD NOT be used.


Forgot to relate to the first code snippet. I wouldn't use forEach at all. Since you are collecting the elements of the Stream into a List, it would make more sense to end the Stream processing with collect. Then you would need peek in order to set the ID.

List<Entry> updatedEntries = 
    entryList.stream()
             .peek(e -> e.setTempId(tempId))
             .collect (Collectors.toList());

For the second snippet, forEach can execute multiple expressions, just like any lambda expression can :

entryList.forEach(entry -> {
  if(entry.getA() == null){
    printA();
  }
  if(entry.getB() == null){
    printB();
  }
  if(entry.getC() == null){
    printC();
  }
});

However (looking at your commented attempt), you can't use filter in this scenario, since you will only process some of the entries (for example, the entries for which entry.getA() == null) if you do.

Answer from Eran on Stack Overflow
🌐
CodingTechRoom
codingtechroom.com › question › java-8-lambda-streams-foreach-multiple-statements
How to Use Java 8 Lambda Streams with forEach for Multiple Statements? - CodingTechRoom
In Java 8, the forEach method can indeed execute only a single statement per Lambda expression. However, you can achieve multiple operations within this context by using a block body or by breaking the operations into smaller functional methods that encapsulate the required logic.
Discussions

lambda - More then 1 command in the Java 8 foreach function - Stack Overflow
Is it possible to use more than one command in the Map.foreach function that got introduced with Java 8? ... Lets pretend that k are Strings and v are Sets. ... The answer is the same for whenever you want to combine multiple statements into a block of code -- use curly braces: map.forEach((k, ... More on stackoverflow.com
🌐 stackoverflow.com
January 1, 2016
Java 8 Lambda Stream forEach with two if-statements - Stack Overflow
I'm trying to refactoring some for-loops into lambda expressions, most of them are working fine, but I'm struggling with for-loops that contains two if-statements. Code for (B2KTransactio... More on stackoverflow.com
🌐 stackoverflow.com
java - multiple lines of code in stream.forEach - Stack Overflow
Yea I'm a lambda novice to be honest, but I've been doing Java for 10 years so I figured I would give a solution with what I know. 2015-12-10T18:41:49.177Z+00:00 ... @vaxquis I didn't mention lambdas in my answer and the question didn't require an answer use lambdas. So I'm not sure why you want me to add it to my answer. 2015-12-10T19:58:35.823Z... More on stackoverflow.com
🌐 stackoverflow.com
Java 8 Lambda Stream forEach with multiple statements
Java 8 Lambda Stream forEach with multiple statements Forgot to relate to the first code snippet. I wouldn't use forEach More on exchangetuts.com
🌐 exchangetuts.com
0
352
People also ask

Can we use forEach in streams Java?
Stream forEach() method in Java with examplesStream forEach(Consumer action) performs an action for each element of the stream. Stream forEach(Consumer action) is a terminal operation i.e, it may traverse the stream to produce a result or a side-effect.
🌐
exchangetuts.com
exchangetuts.com › java-8-lambda-stream-foreach-with-multiple-statements-1639573690324507
Java 8 Lambda Stream forEach with multiple statements
Is stream forEach better than for-loop?
forEach() can be implemented to be faster than for-each loop, because the iterable knows the best way to iterate its elements, as opposed to the standard iterator way. So the difference is loop internally or loop externally.
🌐
exchangetuts.com
exchangetuts.com › java-8-lambda-stream-foreach-with-multiple-statements-1639573690324507
Java 8 Lambda Stream forEach with multiple statements
🌐
javathinking
javathinking.com › blog › java-8-lambda-stream-foreach-with-multiple-statements
Java 8 Lambda Stream: How to Use forEach with Multiple Statements Effectively – A Beginner's Guide
Using forEach() with multiple statements in Java 8 streams allows you to combine actions like transformation, logging, and validation into a single concise block.
🌐
CopyProgramming
copyprogramming.com › howto › java-multiple-statements-in-foreach-java-8
Java: Using Java 8 foreach loop to execute multiple statements
March 25, 2023 - My proposed solution involves implementing your code with JDK 8 streams, lambda expressions, and method references. Instead of if-statements, I utilize filter predicates to solve the issue.
🌐
HowToDoInJava
howtodoinjava.com › home › java 8 › java foreach()
Java forEach() with Examples - HowToDoInJava
February 6, 2023 - List<String> list = Arrays.asList("Alex", "Brian", "Charles"); list.forEach(System.out::println); The program output. ... We can also create custom Consumer action that can execute multiple statements or perform complex logic.
🌐
Medium
neesri.medium.com › master-in-java-8-foreach-48ac3fc940dc
Master in the forEach() Method in Java 8 | by A cup of JAVA coffee with NeeSri | Medium
August 3, 2024 - Using curly braces within the forEach method in Java 8 allows you to include multiple statements or more complex logic within the lambda expression.
Find elsewhere
🌐
Stack Overflow
stackoverflow.com › questions › 40548483 › java-8-lambda-stream-foreach-with-two-if-statements
Java 8 Lambda Stream forEach with two if-statements - Stack Overflow
result.forEach(b2kTransactionDTO -> { //Generate loyaltyMatchId based on transaction input String loyaltyMatchId = getLoyaltyMatchIdBasedOnTransactionDTO(b2kTransactionDTO); if (loyaltyMatchIdAmountMap.containsKey(loyaltyMatchId)) { BigDecimal cashback = loyaltyMatchIdAmountMap.get(loyaltyMatchId); b2kTransactionDTO.addLoyaltyPoints(cashback); } String loyaltyMatchInsuranceId = getLoyaltyMatchInsuranceIdBasedOnTransactionDTO(b2kTransactionDTO); if (loyaltyMatchInsuranceIdAmountMap.containsKey(loyaltyMatchInsuranceId)) { BigDecimal cashback = loyaltyMatchInsuranceIdAmountMap.get(loyaltyMatchInsuranceId); b2kTransactionDTO.addLoyaltyPoints(cashback); } });
Top answer
1 of 3
8

There are two completely different things you should ask here:

a) how do I place multiple lines of code in stream.forEach()?

b) what should I do to count the number of lines in a Stream?

The question b) is answered already by other posters; on the other hand, the general question a) has a quite different answer:

use a (possibly multi-line) lambda expression or pass a reference to multi-line method.

In this particular case, you'd either declare i a field or use a counter/wrapper object instead of i.

For example, if you want to have multiple lines in forEach() explicitly, you can use

class Counter { // wrapper class
    private int count;
    public int getCount() { return count; }
    public void increaseCount() { count++; }
}

and then

Counter counter = new Counter();
lines.stream().forEach( e -> {
    System.out.println(e);
    counter.increaseCounter(); // or i++; if you decided i is worth being a field
} );

Another way to do it, this time hiding those multiple lines in a method:

class Counter { // wrapper class
    private int count;
    public int getCount() { return count; }
    public void increaseCount( Object o ) {
        System.out.println(o);
        count++;
    }
}

and then

Counter counter = new Counter();
lines.stream().forEach( counter::increaseCount );

or even

Counter counter = new Counter();
lines.stream().forEach( e -> counter.increaseCount(e) );

The second syntax comes in handy if you need a consumer having more than one parameter; the first syntax is still the shortest and simplest though.

2 of 3
6

The forEach method takes an instance of any class that implements Consumer. So here is an example of using a custom Consumer implementation that keeps up with the count. Later you can call getCount() on the Consumer implementation to get the count.

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerDemo {
    public static void main(String[] args) {
        List<String> lines = new ArrayList<String>();
        lines.add("line 1");
        lines.add("line 2");

        MyConsumer countingConsumer = new MyConsumer();
        lines.stream().forEach(countingConsumer);
        System.out.println("Count: " + countingConsumer.getCount());
    }

    private static class MyConsumer implements Consumer<String> {
        private int count;

        @Override
        public void accept(String t) {
            System.out.println(t);
            count++;
        }

        public int getCount() {
            return count;
        }
    }
}
🌐
Java67
java67.com › 2016 › 01 › how-to-use-foreach-method-in-java-8-examples.html
10 Examples of forEach() method in Java 8 | Java67
You can read more about that in the Collections to Streams in Java 8 Using the Lambda Expressions course on Pluralsight, which provides an in-depth explanation of new Java 8 features. So far you have both basic and advanced examples of using the forEach() method, first with simply iterating over each element and then along with using the filter() method, Let's see one more example of the forEach() method along with the map() function, which is another key functionality of Stream API.
Top answer
1 of 4
38

It's fairly deeply nested but it doesn't seem exceptionally difficult.

The first observation is that if a for-loop translates into a stream, nested for-loops can be "flattened" into a single stream using flatMap. This operation takes a single element and returns an arbitrary number elements in a stream. I looked up and found that StandardServer.findServices() returns an array of Service so we turn this into a stream using Arrays.stream(). (I make similar assumptions for Engine.findChildren() and Host.findChildren().

Next, the logic within each loop does an instanceof check and a cast. This can be modeled using streams as a filter operation to do the instanceof followed by a map operation that simply casts and returns the same reference. This is actually a no-op but it lets the static typing system convert a Stream<Container> to a Stream<Host> for example.

Applying these transformations to the nested loops, we get the following:

public List<ContextInfo> list() {
    final List<ContextInfo> list = new ArrayList<ContextInfo>();
    final StandardServer server = getServer();

    Arrays.stream(server.findServices())
        .filter(service -> service.getContainer() instanceof Engine)
        .map(service -> (Engine)service.getContainer())
        .flatMap(engine -> Arrays.stream(engine.findChildren()))
        .filter(possibleHost -> possibleHost instanceof Host)
        .map(possibleHost -> (Host)possibleHost)
        .flatMap(host -> Arrays.stream(host.findChildren()))
        .filter(possibleContext -> possibleContext instanceof Context)
        .map(possibleContext -> (Context)possibleContext)
        .forEach(context -> {
            // copy to another object -- not the important part
            final ContextInfo info = new ContextInfo(context.getPath());
            info.setThisPart(context.getThisPart());
            info.setNotImportant(context.getNotImportant());
            list.add(info);
        });
    return list;
}

But wait, there's more.

The final forEach operation is a slightly more complicated map operation that converts a Context into a ContextInfo. Furthermore, these are just collected into a List so we can use collectors to do this instead of creating and empty list up front and then populating it. Applying these refactorings results in the following:

public List<ContextInfo> list() {
    final StandardServer server = getServer();

    return Arrays.stream(server.findServices())
        .filter(service -> service.getContainer() instanceof Engine)
        .map(service -> (Engine)service.getContainer())
        .flatMap(engine -> Arrays.stream(engine.findChildren()))
        .filter(possibleHost -> possibleHost instanceof Host)
        .map(possibleHost -> (Host)possibleHost)
        .flatMap(host -> Arrays.stream(host.findChildren()))
        .filter(possibleContext -> possibleContext instanceof Context)
        .map(possibleContext -> (Context)possibleContext)
        .map(context -> {
            // copy to another object -- not the important part
            final ContextInfo info = new ContextInfo(context.getPath());
            info.setThisPart(context.getThisPart());
            info.setNotImportant(context.getNotImportant());
            return info;
        })
        .collect(Collectors.toList());
}

I usually try to avoid multi-line lambdas (such as in the final map operation) so I'd refactor it into a little helper method that takes a Context and returns a ContextInfo. This doesn't shorten the code at all, but I think it does make it clearer.

UPDATE

But wait, there's still more.

Let's extract the call to service.getContainer() into its own pipeline element:

    return Arrays.stream(server.findServices())
        .map(service -> service.getContainer())
        .filter(container -> container instanceof Engine)
        .map(container -> (Engine)container)
        .flatMap(engine -> Arrays.stream(engine.findChildren()))
        // ...

This exposes the repetition of filtering on instanceof followed by a mapping with a cast. This is done three times in total. It seems likely that other code is going to need to do similar things, so it would be nice to extract this bit of logic into a helper method. The problem is that filter can change the number of elements in the stream (dropping ones that don't match) but it can't change their types. And map can change the types of elements, but it can't change their number. Can something change both the number and types? Yes, it's our old friend flatMap again! So our helper method needs to take an element and return a stream of elements of a different type. That return stream will contain a single casted element (if it matches) or it will be empty (if it doesn't match). The helper function would look like this:

<T,U> Stream<U> toType(T t, Class<U> clazz) {
    if (clazz.isInstance(t)) {
        return Stream.of(clazz.cast(t));
    } else {
        return Stream.empty();
    }
}

(This is loosely based on C#'s OfType construct mentioned in some of the comments.)

While we're at it, let's extract a method to create a ContextInfo:

ContextInfo makeContextInfo(Context context) {
    // copy to another object -- not the important part
    final ContextInfo info = new ContextInfo(context.getPath());
    info.setThisPart(context.getThisPart());
    info.setNotImportant(context.getNotImportant());
    return info;
}

After these extractions, the pipeline looks like this:

    return Arrays.stream(server.findServices())
        .map(service -> service.getContainer())
        .flatMap(container -> toType(container, Engine.class))
        .flatMap(engine -> Arrays.stream(engine.findChildren()))
        .flatMap(possibleHost -> toType(possibleHost, Host.class))
        .flatMap(host -> Arrays.stream(host.findChildren()))
        .flatMap(possibleContext -> toType(possibleContext, Context.class))
        .map(this::makeContextInfo)
        .collect(Collectors.toList());

Nicer, I think, and we've removed the dreaded multi-line statement lambda.

UPDATE: BONUS CHALLENGE

Once again, flatMap is your friend. Take the tail of the stream and migrate it into the last flatMap before the tail. That way the host variable is still in scope, and you can pass it to a makeContextInfo helper method that's been modified to take host as well.

    return Arrays.stream(server.findServices())
        .map(service -> service.getContainer())
        .flatMap(container -> toType(container, Engine.class))
        .flatMap(engine -> Arrays.stream(engine.findChildren()))
        .flatMap(possibleHost -> toType(possibleHost, Host.class))
        .flatMap(host -> Arrays.stream(host.findChildren())
                               .flatMap(possibleContext -> toType(possibleContext, Context.class))
                               .map(ctx -> makeContextInfo(ctx, host)))
        .collect(Collectors.toList());
2 of 4
26

This would be my version of your code using JDK 8 streams, method references, and lambda expressions:

server.findServices()
    .stream()
    .map(Service::getContainer)
    .filter(Engine.class::isInstance)
    .map(Engine.class::cast)
    .flatMap(engine -> Arrays.stream(engine.findChildren()))
    .filter(Host.class::isInstance)
    .map(Host.class::cast)
    .flatMap(host -> Arrays.stream(host.findChildren()))
    .filter(Context.class::isInstance)
    .map(Context.class::cast)
    .map(context -> {
        ContextInfo info = new ContextInfo(context.getPath());
        info.setThisPart(context.getThisPart());
        info.setNotImportant(context.getNotImportant());
        return info;
    })
    .collect(Collectors.toList());

In this approach, I replace your if-statements for filter predicates. Take into account that an instanceof check can be replaced with a Predicate<T>

Predicate<Object> isEngine = someObject -> someObject instanceof Engine;

which can also be expressed as

Predicate<Object> isEngine = Engine.class::isInstance

Similarly, your casts can be replaced by Function<T,R>.

Function<Object,Engine> castToEngine = someObject -> (Engine) someObject;

Which is pretty much the same as

Function<Object,Engine> castToEngine = Engine.class::cast;

And adding items manually to a list in the for loop can be replaced with a collector. In production code, the lambda that transforms a Context into a ContextInfo can (and should) be extracted into a separate method, and used as a method reference.

🌐
Jsparrow
jsparrow.github.io › rules › enhanced-for-loop-to-stream-for-each.html
Replace For-Loop with Iterable::forEach | jSparrow Documentation
stringList.forEach(value -> { int length = value.length(); if (length > 2) { length /= 2; } print(value.substring(0, length)); });
🌐
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.
🌐
ZetCode
zetcode.com › java › foreach
Java forEach - forEach on Java lists, maps, sets
June 17, 2025 - The forEach method executes a specified action for each element within an Iterable object, continuing until all elements are processed or an exception occurs. Introduced in Java 8, this method offers developers a modern, concise alternative to traditional looping constructs, simplifying the ...
🌐
IQCode
iqcode.com › code › java › java-lambda-foreach-multiple-statements
java lambda foreach multiple statements Code Example
November 13, 2021 - java lambda foreach that mutable foreach lambda multiple arguments java java foreach lambda multiple lines multiple statements in foreach java 8 multiple statements in stream foreach java stream multiple foreach stream foreach more than on line lambda with 2 variables java foreach stream java ...
🌐
Mkyong
mkyong.com › home › java8 › java 8 foreach examples
Java 8 forEach examples | mkyong.com
December 4, 2020 - In Java 8, we can use the new forEach to loop or iterate a Map, List, Set, or Stream. Topics Loop a Map Loop a List forEach and Consumer forEach
🌐
Blogger
javarevisited.blogspot.com › 2015 › 09 › java-8-foreach-loop-example.html
Java 8 forEach() Loop Example
Java 8 has introduced a new way to loop over a List or Collection, by using the forEach() method of the new Stream class. You can iterate over any Collection like List, Set, or Map by converting them into a java.util.sttream.Stream instance ...
🌐
Adam-bien
adam-bien.com › roller › abien › entry › java_8_from_a_for
Java 8: From a for-loop to forEach statement
January 17, 2014 - List<String> strings = new ... forEach statement: List<String> strings = new ArrayList<>(); strings.stream().forEach((string) -> { System.out.println("Content: " + string); }); With the "functional looping style" any pre- or post-processing like filtering, grouping or even parallelization can ...
🌐
Exchangetuts
exchangetuts.com › java-8-lambda-stream-foreach-with-multiple-statements-1639573690324507
Java 8 Lambda Stream forEach with multiple statements
Consider separating them into 2 statements (using static import of toList()): entryList.forEach(e->e.setTempId(tempId)); List<Entry> updatedEntries = entryList.stream() .map(e->entityManager.update(entry, entry.getId())) .collect(toList());
🌐
Solved
code.i-harness.com › en › q › 1db0359
with - multi line lambda java 8 - Solved
For the second snippet, forEach can execute multiple expressions, just like any lambda expression can :