How to Check if a String Contains a Substring in Java
Check for a substring in a Java string with contains, indexOf, startsWith, endsWith, and regex.
How to Check if a String Contains a Substring in Java
Checking whether one String appears inside another is one of the most common text tasks in Java. The right tool depends on what you actually need: a plain yes/no answer, the position of the match, an anchored check at the start or end, or a flexible pattern. This chapter walks through each idiomatic approach and when to reach for it.
The default: String.contains
When you only need a boolean — "is this fragment in there or not?" — contains is the clearest choice:
String text = "The quick brown fox";
boolean hasFox = text.contains("fox"); // true
boolean hasCat = text.contains("cat"); // falsecontains accepts any CharSequence (so String, StringBuilder, etc.) and returns true if the argument occurs anywhere in the string. It is case-sensitive: text.contains("FOX") is false. One edge case worth remembering — the empty string is contained in every string, so text.contains("") is always true.
When you need the position: indexOf
contains is actually implemented on top of indexOf. If you need to know where a match starts (or want to scan for repeats), call indexOf directly:
String text = "The quick brown fox";
int at = text.indexOf("brown"); // 10
int no = text.indexOf("bird"); // -1indexOf returns the zero-based index of the first match, or -1 when the substring is absent. The classic membership idiom is text.indexOf("brown") >= 0, which is equivalent to contains but also hands you the position for free. There is also lastIndexOf for searching from the end, and an overload that takes a starting offset for finding subsequent matches in a loop.
Anchored checks: startsWith and endsWith
If you specifically care about the beginning or end of a string — file extensions, URL prefixes, protocol schemes — startsWith and endsWith express the intent directly and read better than slicing:
String file = "report.pdf";
boolean isPdf = file.endsWith(".pdf"); // true
boolean isHttp = "https://w3docs.com".startsWith("https://"); // trueThese are faster and clearer than indexOf(prefix) == 0, because they stop comparing as soon as a character differs and never scan the whole string.
Case-insensitive and pattern matching
contains has no case-insensitive overload. The simplest fix is to normalise both sides:
boolean ci = text.toLowerCase().contains("FOX".toLowerCase()); // trueFor more than a literal fragment — alternatives, wildcards, word boundaries — use a regular expression. Pattern.compile(...).matcher(text).find() returns true if the pattern matches anywhere, and CASE_INSENSITIVE handles casing without allocating lowercase copies:
import java.util.regex.Pattern;
boolean found = Pattern.compile("fox", Pattern.CASE_INSENSITIVE)
.matcher(text).find();| Approach | Returns | Use when |
|---|---|---|
contains | boolean | Plain membership, literal text |
indexOf | int (position, or -1) | You need where the match is |
startsWith / endsWith | boolean | Anchored at start or end |
Pattern.find | boolean | Patterns, alternatives, case-insensitive |
A worked example
This program runs the four approaches side by side over one sentence, including the case-sensitivity gotcha and the empty-string edge case.
What to take from the run:
contains "brown"printstrueandcontains "cat"printsfalse—containsis the direct boolean membership test for literal text.contains "FOX"printsfalseeven though the word "fox" is present, provingcontainsis case-sensitive; the normalisedcontains "FOX" (ci)line printstrue.indexOf "fox"prints16, the zero-based start position of the match, whileindexOf "bird"prints-1to signal "not found" — that-1is the sentinel you test against.startsWith "The"andendsWith "dog"both printtrue, showing the anchored checks reading the very start and very end of the sentence.contains ""printstrue, the reminder that every string contains the empty string — guard against empty inputs if that would be a bug.
Practice
What does text.indexOf('bird') return when 'bird' is not present in the string text?