Put a label on the outer loop and break to it: outer: for (…) { for (…) { if (found) break outer; } }. A plain break only leaves the innermost loop. When the loops are a search, moving them into a method and using return is often cleaner still.
Java has no break 2 and no goto, but it has something better suited to the job: labeled statements. Name a loop with outer: and break outer; ends it from any depth, while continue outer; jumps straight to its next iteration. This page runs one search through every approach: find the first pair of numbers that adds up to a target. It covers the labeled break and continue, the extract-a-method version that many style guides prefer, the boolean flag you will meet in older code, and a streams version for when the nested loop is really just a search. Each example runs on this page: hit Run, then edit the code and run it again.
1Labeled break: break outer;Recommended
A label is an identifier followed by a colon, written right before a loop. break outer; ends the loop named outer, however deeply nested the break is, and execution continues with the first statement after that loop. The snippet runs the same pair search twice: first with a plain break, which only leaves the inner loop, then with the label.
Output
The plain version prints plain break found 8 + 5 and plain break found 1 + 12: its break ended the inner loop, and the outer loop moved on to the next i and found a second pair. The labeled version prints break outer found 8 + 5 at indexes 1 and 4 and pairs checked: 8, so it stopped at the first match. The name outer is only a convention; any identifier works, and a descriptive one such as search: reads better when there are several labels. Labels have been in Java since 1.0, so this works on every version.
2Labeled continue: continue outer;
Sometimes you don't want to stop everything, only to abandon the current outer item. continue outer; does that: it skips the rest of the inner loop and the rest of the outer loop's body, then starts the outer loop's next iteration. Here an order is shipped only if none of its items is out of stock.
Output
Prints order 1 shipped: [keyboard, mouse], order 2 skipped: webcam is out of stock and order 3 shipped: [mouse, cable]. Change continue outer; to a plain continue; and the bug appears: order 2 prints the "skipped" line and then order 2 shipped: [monitor, webcam, cable] as well, because a plain continue only moves on to the next item, and the shipping line still runs once the inner loop finishes. continue needs a loop label; pointing it at a label on a plain block is a compile error (not a loop label).
3Move the loops into a method and return
return leaves every loop in the method at once, so putting the nested loops in their own method gets you out without a label. It is often the cleanest option, because the method name describes what the loops are for and the result comes back as a value instead of through variables declared before the loops. Return an Optional so "not found" is part of the type rather than a null or -1 the caller has to remember. The Pair record needs Java 16+.
Output
Prints Optional[Pair[first=8, second=5]], Optional.empty, then 3 + 12 = 15. The method can be reused and tested on its own, which a labeled block inside main cannot. On Java 8 to 15, where records don't exist, return an int[] of the two values or a small final class instead. When the loops need to change several local variables, a method gets awkward; that is where the labeled break is the better fit.
4A boolean flag
Before labels became familiar, the usual trick was a boolean that the inner loop sets and the outer loop checks. It works, and you will see it in a lot of existing code, but every loop above the inner one has to test the flag, and the results have to live in variables declared before the loops. This one looks for a value in a 2D grid.
Output
Prints 5 is at row 1, column 1. The inner break is still needed, otherwise the inner loop finishes its row after the match; the flag only stops the outer loop, through the !found in its condition (an if (found) break; right after the inner loop does the same). With three or more levels every level needs that check, and row and col have to be declared outside the loops with placeholder values, which is why break outer; or a method is usually the better choice.
5Streams: anyMatch() and findFirst()
When the nested loop only exists to answer "is there a match?" or "what is the first match?", streams can say that directly (Java 8+). anyMatch() and findFirst() are short-circuiting: they stop pulling elements as soon as they have an answer, so nothing needs to break. Nest one anyMatch in another for a yes/no answer, use flatMap plus findFirst to get the matching pair, and flatten a 2D array with flatMapToInt to search every cell.
Output
Prints true, Optional[Pair[first=8, second=5]], then true. Adding a counter inside the predicates shows both searches stop after 8 checks, the same as break outer; in section 1. That relies on flatMap being lazy, which it has been since Java 10; on Java 8 and 9 the flatMap version still returns the right pair but runs the inner stream that holds the match to its end. The lambdas can only read numbers and target because they are effectively final. For the single-array version of this question, see check if an array contains a value.
6Which should you use?
| Method | Stops every loop | Extra code | Best for |
|---|---|---|---|
| break outer; | Yes | One label | The direct answer; loops that update local variables |
| continue outer; | No, skips to the next outer item | One label | Abandoning one outer item, then carrying on |
| Method + return | Yes | A method | Searches that produce a value; reusable, testable code |
| boolean flag | Yes, if every level checks it | A variable + checks | Matching the style of older code |
| anyMatch() / findFirst() | Yes, short-circuits | None, but a different style | Pure yes/no or first-match searches |
| break | No, innermost loop only | None | Leaving a single loop (or a switch) |
Frequently asked questions
What does a plain break do inside nested loops in Java?
It ends only the innermost loop (or switch) that contains it. The outer loop goes on to its next iteration, so a search can find a second match: in section 1 the plain version prints both 8 + 5 and 1 + 12. To leave both loops, label the outer one and write break outer;, or put the loops in a method and return.
Is a labeled break the same as goto?
No. goto is a reserved keyword in Java with no meaning, so even int goto = 1; fails to compile. A labeled break can only jump forward, to the end of a statement that encloses it, and a labeled continue only to the next iteration of an enclosing loop. You cannot jump into a loop or backwards to an arbitrary line, which is what made goto hard to follow.
How do I break out of forEach or a stream in Java?
You cannot: the body of forEach is a lambda, not a loop, so break inside it is a compile error, break outside switch or loop, and return only ends the current call. Use a short-circuiting operation that describes the search, such as anyMatch(), findFirst() or takeWhile() (Java 9+), or switch to an ordinary for loop where break works.
Why does break inside a switch not exit my loop?
Because break binds to the nearest enclosing loop or switch, and inside a classic case that is the switch. The loop simply moves on to its next element. Label the loop (loop: for (...)) and write break loop; inside the case to leave the loop from within the switch.
Can I put a label on a block instead of a loop?
Yes. Any statement can carry a label, including a plain { ... } block, and break label; jumps to the end of that block, which works as an early exit without a loop. continue is different: it needs a loop label, and pointing it at a block label fails with not a loop label.
Should I use a labeled break or extract a method?
Both are fine. If the nested loops compute one result, such as a found item or a yes/no answer, a method that returns it is usually clearer, because the name explains the loops and the caller gets a value. If the loops update several local variables or sit in the middle of a longer method, a labeled break avoids passing all that state around. Avoid the boolean flag in new code; it adds a check to every level for the same effect.
Run it yourself
Open any of these in the full Java editor to tweak, run and share them.
Java playground