contains()

Returns true when a String includes a given sequence of chars anywhere, with an exact, case-sensitive comparison.

Signature
public boolean contains(CharSequence s)
  • Added in Java Java 5

Parameters

ParameterTypeDescription
$sCharSequenceThe 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 always true. A search box that sends an empty term matches everything, which may or may not be what you want.
  • s.contains(null) throws NullPointerException. Optional query parameters are often null, so guard with term != 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

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · java
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 429

A 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.

Example · java
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.

Example · java
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

  • Be the first to comment on this lesson.