skip to content

What is the difference between greedy and reluctant (lazy) quantifiers in Java regex, and when does it matter?

level: middleimportance: must knowfreq 68%

answer

  1. Greedy = grab max, then backtrack (default)
  2. Lazy = grab min, then expand; add trailing ?
  3. *? +? ?? {n,m}? are the lazy forms
  4. <.*> matches whole; <.*?> matches first tag
  5. Often BOTH match — just different substrings

basics

~20 s

Greedy quantifiers (the default) grab as much text as possible, then give some back if needed. Lazy quantifiers, written with a trailing ?, grab as little as possible and only take more if forced. They differ in how much text each match consumes.

solid answer

~50 s

By default Java quantifiers are greedy: they consume as much of the input as they can, then backtrack (give characters back) only if that's required for the rest of the pattern to match. Adding a `?` after a quantifier makes it reluctant, or lazy: it consumes as little as possible and expands only when the rest of the pattern would otherwise fail. The classic example is matching `<a><b>` with `<.*>` versus `<.*?>`. Greedy `<.*>` matches the whole string `<a><b>` because `.*` grabs everything and then backtracks just enough to find a final `>`. Lazy `<.*?>` matches only `<a>` because `.*?` stops at the first `>`. This matters whenever delimiters can repeat: parsing tags, quoted strings, or extracting the shortest span between markers. Both produce a match here, but a different one — so choosing the wrong mode silently returns the wrong substring rather than failing.

go deeper

for a junior

Knows greedy is default and lazy uses a trailing ?; can state that they consume different amounts.

for a middle

Can trace the <.> vs <.?> example and explain backtracking and the shortest-span use case.

for a senior

Reasons about when each mode produces a wrong-but-successful match and ties laziness to backtracking shape and parsing intent.

for a principal

Weighs greedy vs lazy vs a precise negated-class alternative ([^>]*) for correctness and performance, and codifies guidance for the team.

## Setup: what 'matching' actually does When a regex engine matches, it walks through the pattern token by token and tries to consume input characters. Quantifiers (`*`, `+`, `?`, `{n,m}`) introduce a choice: a repeatable atom could match few or many times. The **mode** of the quantifier decides which choice the engine tries *first*. ## Greedy (the default) A **greedy** quantifier first tries to match as many repetitions as possible. `.*` (dot = any char, `*` = zero-or-more) will initially swallow the entire rest of the line. Only then does the engine continue with the next part of the pattern. If that next part fails to match, the engine **backtracks**: it gives back one character at a time from the greedy chunk and retries, until either the pattern matches or there's nothing left to give back. Example on input `<a><b>` with pattern `<.*>`: 1. Match the literal `<`. 2. `.*` greedily grabs `a><b>` (everything to end). 3. The pattern still needs a final `>`, but we're at end of input — fail. 4. Backtrack: `.*` gives back `>`, now needs `>` at that position — success. 5. Result: the whole `a><b` is inside, so the overall match is `<a><b>`. ## Reluctant / lazy (add a trailing `?`) A **reluctant** quantifier (written `*?`, `+?`, `??`, `{n,m}?`) does the opposite: it first matches as *few* repetitions as it can, then **expands** one character at a time only when the rest of the pattern fails. Same input `<a><b>` with pattern `<.*?>`: 1. Match `<`. 2. `.*?` matches **zero** characters first. 3. Need `>` — but next char is `a`, fail. 4. Expand `.*?` by one to `a`; need `>` — next is `>`, success. 5. Result: overall match is `<a>` only. ## Why the trailing `?` is overloaded Note the symbol `?` plays two roles. Alone it's the zero-or-one quantifier. *After another quantifier* it's the laziness modifier. So `a?` = optional `a`; `a*?` = lazy zero-or-more; `a??` = lazy optional. ## When it matters - **Extracting the shortest span between delimiters** (HTML/XML tags, `"..."` strings, `{...}` placeholders): lazy gives you each individual span; greedy gives you one giant span from the first opener to the last closer. - **Performance**: greedy + backtracking can be slow on pathological input (see catastrophic backtracking). Lazy isn't automatically faster, but it changes the backtracking shape. - It is **not** about whether a match exists — on many inputs both modes match, just different substrings. That's what makes the bug subtle: no exception, just a wrong group. ## Practical Java note Nothing in the API changes — you express the mode purely in the pattern string. `Pattern.compile("<.*?>")` versus `Pattern.compile("<.*>")`. Use `Matcher.find()` in a loop to pull each tag; with the lazy version you get one tag per iteration.

  • On input '<a><b>', what does <.*> match and what does <.*?> match?
    Greedy <.*> matches the entire string '<a><b>' (backtracks to the last >); lazy <.*?> matches just '<a>' (stops at the first >).
  • What does a trailing ? after a + mean, as in a+?
    It makes the + reluctant/lazy: match one-or-more a's but as few as possible, expanding only when forced.

saying these in an interview costs you the question

  • Saying lazy means 'no match' when greedy would match — usually both match, differently
  • Confusing the optional ? with the laziness ?
  • Assuming lazy is always faster than greedy
  • Thinking greedy never gives characters back (it backtracks)

context