What are possessive quantifiers in Java, how do they differ from greedy ones, and what are atomic groups?
answer
- Append + = possessive: max, then NEVER backtrack
- Atomic group (?>...) = group-level possessive
- X*+ ≈ (?>X*)
- Prevents catastrophic backtracking / ReDoS
- Can fail a match greedy would find
- Safe for "[^\"]*+\" (class excludes delimiter)
basics
~20 sA possessive quantifier (written with a trailing +, like a++) grabs as much as it can and then never gives any of it back — it disables backtracking for that part. An atomic group (?>...) does the same thing for a whole subpattern.
solid answer
~50 sPossessive quantifiers are a third mode alongside greedy and lazy, written by appending `+` to a quantifier: `*+`, `++`, `?+`, `{n,m}+`. Like greedy, they consume as much as possible; unlike greedy, they refuse to backtrack — once matched, those characters are locked in. If the rest of the pattern can't match with what's left, the whole match fails immediately rather than the engine trying smaller possibilities. An atomic group `(?>...)` generalizes this to any subpattern: whatever the group matches is final, no internal backtracking. The practical benefit is performance and safety: possessive quantifiers and atomic groups prevent catastrophic backtracking, where ambiguous nested quantifiers cause exponential time on certain inputs. The trade-off is that they can cause a match to fail that a greedy quantifier would have found by backtracking, so you use them when you know backtracking into that span is never desirable — for example `"[^"]*+"` for a quoted string, where the inner class already excludes the delimiter.
code
java · 12 linesimport java.util.regex.Pattern;
var greedy = Pattern.compile("a+a"); // backtracks: matches "aaa"
var possessive = Pattern.compile("a++a"); // locks a's: FAILS on "aaa"
System.out.println(greedy.matcher("aaa").matches()); // true
System.out.println(possessive.matcher("aaa").matches()); // false
// Safe, linear-time quoted-string matcher: the inner class
// can never match the delimiter, so backtracking is pointless.
var quoted = Pattern.compile("\"[^\"]*+\"");
System.out.println(quoted.matcher("\"hello world\"").matches()); // truego deeper
Aware that a third quantifier mode exists (possessive) but may not use it; recognizes the trailing + syntax.
Can describe possessive as 'no backtracking' and relate it to atomic groups; knows it can cause failures.
Explains catastrophic backtracking/ReDoS and uses possessive quantifiers or atomic groups deliberately on delimited spans and untrusted input.
Designs validation patterns to be linear-time by construction (negated classes, atomic groups), reviews regexes for ReDoS, and sets team policy on untrusted-input patterns.
## Three modes recap Every Java quantifier comes in three flavours, controlled by what (if anything) you append: - **Greedy** (nothing appended): match max, then backtrack if needed. `a*` - **Reluctant / lazy** (append `?`): match min, then expand if needed. `a*?` - **Possessive** (append `+`): match max, then **never give back**. `a*+` So the suffix grid is: `*`, `*?`, `*+`; `+`, `+?`, `++`; `?`, `??`, `?+`; `{n,m}`, `{n,m}?`, `{n,m}+`. ## What 'never give back' means Recall that a greedy quantifier, after grabbing everything, will **backtrack** — surrender characters one by one — so the rest of the pattern can match. A **possessive** quantifier skips that surrender entirely. It grabs the maximum and locks it. If the remainder of the pattern then fails, the engine does **not** retry with fewer characters; the overall match just fails. Example: pattern `a++a` against `"aaa"`. - `a++` greedily takes all three `a`s and locks them. - The pattern still needs one more `a`, but input is exhausted, and `a++` won't give one back. - Result: **no match** — even though greedy `a+a` would match by giving one `a` back. That shows the danger: possessive can turn a would-be match into a failure. You use it only when backtracking into that span is genuinely pointless. ## Atomic groups: `(?>...)` An **atomic group** `(?>X)` matches `X` and then throws away all the backtracking positions inside it — whatever `X` matched is final. It's the group-level equivalent of a possessive quantifier. In fact `X*+` is conceptually `(?>X*)`. Atomic groups are useful when the thing you want to make non-backtracking is a whole alternation or sequence, e.g. `(?>\d+|\w+)`. ## Catastrophic backtracking — the problem they solve Consider `(a+)+b` (or the classic `(a|aa)+`). On an input like `"aaaaaaaaaaaaaaaaaaaa"` with **no** trailing `b`, the engine has exponentially many ways to partition the `a`s between the inner `+` and outer `+`. A greedy engine will try them all before concluding failure — time grows like 2^n. This is **catastrophic (or exponential) backtracking**, the basis of ReDoS (regular-expression denial of service): a small malicious input hangs the thread. Making the inner quantifier possessive — `(a+)++b` or rewriting with an atomic group `(?>a+)+b` — removes the redundant partitions: once the inner `a+` has eaten its run, it won't hand `a`s back to be re-divided, so failure is detected in linear time. ## When to reach for them - **Quoted/delimited spans** where the inner class already excludes the delimiter: `"[^"]*+"`. Since `[^"]` can never match `"`, backtracking could never help, so locking it is free correctness + speed. - **Guarding untrusted input** against ReDoS in validation patterns. - **Disambiguating nested quantifiers** that would otherwise overlap. ## When NOT to If the rest of the pattern legitimately needs to claw back characters the quantifier ate, possessive will break the match. Test on real inputs. Prefer rewriting the pattern to be unambiguous (e.g. negated character classes) over sprinkling possessives blindly. ## Java specifics All three modes and atomic groups are supported by `java.util.regex` (since Java 1.4 for possessive/atomic). They're pure pattern syntax — no API change. `Pattern.compile("\"[^\"]*+\"")` is a typical safe quoted-string matcher.
- Why does a++a fail to match 'aaa' while a+a succeeds?a++ possessively locks all three a's and won't give any back; the trailing a then has no input left, so the whole match fails. Greedy a+a backtracks one a so the final a can match.
- How does a possessive quantifier prevent catastrophic backtracking?It removes the redundant backtracking choices in ambiguous nested quantifiers, so the engine doesn't explore exponentially many partitions before declaring failure — turning exponential time into linear.
saying these in an interview costs you the question
- Confusing possessive ++ (the second +) with two one-or-more quantifiers
- Thinking possessive always matches what greedy matches — it can fail where greedy succeeds
- Adding possessive quantifiers everywhere 'for speed' without checking correctness
- Believing atomic groups capture text differently — (?>...) is non-capturing