contains()
Returns true when a String includes a given sequence of chars anywhere, with an exact, case-sensitive comparison.
public boolean contains(CharSequence s)- Added in Java Java 5
Parameters
| Parameter | Type | Description |
|---|---|---|
$s | CharSequence | The text to look for. Any CharSequence works, including String and StringBuilder. |
Return value
booleantrue if this String contains the given sequence of chars, false otherwise. Always true for an empty sequence.
Errors
NullPointerException if s is null.
contains answers one question: does this text appear anywhere in the String? It is exactly indexOf(s.toString()) >= 0 with a clearer name, and it accepts any CharSequence, so a StringBuilder works too.
Exact and case-sensitive
The comparison is char by char. "Googlebot".contains("googlebot") is false. For a case-insensitive check, lowercase both sides with Locale.ROOT. There is no containsIgnoreCase in the JDK. For long texts searched many times, a precompiled Pattern with Pattern.quote(term) and CASE_INSENSITIVE | UNICODE_CASE avoids creating lowercase copies. Without UNICODE_CASE the flag only folds ASCII letters, so É would not match é.
Edge cases
s.contains("")is alwaystrue. A search box that sends an empty term matches everything, which may or may not be what you want.s.contains(null)throwsNullPointerException. Optional query parameters are often null, so guard withterm != null &&.- Calling it on a null String also throws. A constant on the left does not help here as it does with
equals.
Where contains is the wrong tool
It is tempting to check a JSON body with body.contains("\"status\":\"ok\""). It breaks as soon as the server adds a space or reorders fields. Parse the JSON instead. The same goes for security checks: rejecting a path because it contains ".." misses encoded forms such as %2e%2e. Decode the value once, normalise it, for example with Path.normalize(), and then check where it points.
For searching user-facing data in a database, push the filter into SQL (LIKE or full-text search) rather than loading rows and calling contains in Java. In-memory contains is right for small lists, log lines, feature flags and user agent checks.
Example
When to use it
- Skipping analytics for requests whose User-Agent contains a known bot name.
- Filtering an in-memory list of feature flags or tags by a search term.
- Checking that an error message from a dependency mentions a timeout before retrying.
Frequently asked questions
How do I check if a String contains a substring ignoring case?
Lowercase both values with the same locale, usually Locale.ROOT: text.toLowerCase(Locale.ROOT).contains(term.toLowerCase(Locale.ROOT)). The JDK has no containsIgnoreCase; a Pattern compiled from Pattern.quote(term) with CASE_INSENSITIVE | UNICODE_CASE is the alternative (CASE_INSENSITIVE alone only folds ASCII).
Why does contains throw NullPointerException?
Either the String you call it on is null, or the argument is null. contains(null) is not treated as false; check the argument first.
What is the difference between contains and indexOf?
contains returns a boolean and is implemented with indexOf. Use indexOf when you also need the position of the match.
More examples
Filtering log lines
contains and startsWith make readable stream filters for quick log triage. For anything you run every day, structured logs with real fields beat substring searches.
void main() {
List<String> lines = List.of(
"INFO GET /health 200",
"ERROR POST /orders 500 timeout",
"INFO GET /orders/7 200",
"WARN POST /orders 429");
lines.stream()
.filter(l -> l.contains("/orders"))
.filter(l -> !l.startsWith("INFO"))
.forEach(IO::println);
}
// Output:
// ERROR POST /orders 500 timeout
// WARN POST /orders 429A case-insensitive product search
Both sides are lowercased with Locale.ROOT and the query is stripped, so stray spaces and capitals from a search box still match. Fine for a short in-memory list; for a table, filter in SQL.
boolean matches(String text, String query) {
return text.toLowerCase(Locale.ROOT).contains(query.strip().toLowerCase(Locale.ROOT));
}
void main() {
List<String> products = List.of("USB-C Cable", "Laptop Stand", "usb hub", "Desk Lamp");
for (String q : List.of("usb", " STAND ")) {
List<String> hits = products.stream().filter(p -> matches(p, q)).toList();
IO.println("'" + q + "' -> " + hits);
}
}
// Output:
// 'usb' -> [USB-C Cable, usb hub]
// ' STAND ' -> [Laptop Stand]Guarding against a null term
The null guard short-circuits before contains runs. The last two lines show why matching JSON as text is fragile: one extra space and the check fails, so parse the body instead.
void main() {
String body = "{\"status\":\"ok\"}";
String term = null; // e.g. a missing query parameter
IO.println(term != null && body.contains(term));
try {
body.contains(term);
} catch (NullPointerException e) {
IO.println("NullPointerException");
}
IO.println(body.contains("\"status\":\"ok\""));
IO.println(body.contains("\"status\": \"ok\""));
}
// Output:
// false
// NullPointerException
// true
// false
Discussion