Java String contains - Case-Sensitive Payment Filter Fail
Payments marked 'failed' not retried because contains() expected 'Failed' after upgrade.
- String.contains() returns true if the exact substring appears anywhere in the target string, case-sensitively.
- For case-insensitive, normalise both strings to Locale.ROOT before comparing.
- Calling contains() on a null string or passing null both throw NullPointerException.
- Performance: O(n*m) worst case; repeated calls on the same large text should use indexOf with offsets or Aho-Corasick.
- Production trap: A single NPE from .contains() can crash the entire request if not guarded.
String.contains() answers one question: does this string have this other string somewhere inside it? It's a substring check — not an equality check, not a starts-with check. The method is straightforward; the edge cases around null, case-sensitivity, and performance on large strings are where developers trip up.
contains() is in the top 20 Java String methods by frequency of use. It's also in the top 10 sources of NullPointerException in production Java code. Understanding the null contract and the case-sensitivity behaviour up front prevents the bugs I see repeatedly in code review.
contains() Usage, Case-Insensitive Variant, and Null Safety
String.contains() takes a CharSequence (which String implements) and returns true if the argument appears anywhere within the string. The check is case-sensitive. For case-insensitive contains, convert both strings to the same case first — but use Locale.ROOT or Locale.ENGLISH to avoid locale-specific case conversion bugs (the Turkish 'i' problem).
contains() calls with a null check unless you're absolutely certain the reference is non-null.contains() with a null check.Performance of contains() and Alternatives When Speed Matters
contains() internally calls indexOf(), which uses a naive O(nm) algorithm. For most one-off checks on small to medium strings, it's fast enough. But in tight loops over large strings (e.g., processing a 100K-character log line a thousand times), that O(nm) adds up. If you're checking for multiple patterns repeatedly, consider using Aho-Corasick or building a trie. For a single pattern, you can use indexOf() with a starting offset to search incrementally.
If you need to check the same large string for many substrings, cache the result after the first check or convert to a hash-based approach. Never assume contains() is cheap on a gigabyte-sized string.
contains() was called on every line in a 1GB file for filtering.contains() vs matches() vs indexOf() – Choosing the Right Tool
contains() treats its argument as a literal substring. If you need regex pattern matching, use String.matches() or Pattern.compile(). The matches() method checks if the entire string matches the pattern, not just a substring — so you'll need .* around the pattern for a containment check. indexOf() returns the index of the first occurrence, or -1 if not found; use it when you need the position.
Remember: contains() cannot handle regex metacharacters. If you pass "\\d+" to contains(), it looks for the literal backslash-d-plus, not a digit pattern. That's a common trap.
contains() on a phone number field expecting regex validation, and it always returned false for valid numbers.contains() was looking for literal dots and hyphens.contains() only for exact literal substrings; for patterns, use Pattern.find().Pattern.find() or matches() with .*.contains() just a boolean.Using contains() with CharSequence and StringBuilder
The parameter of contains() is CharSequence, not String. This means you can pass StringBuilder, StringBuffer, or any other CharSequence implementation directly – no need to call toString(). That saves an allocation and speeds things up when you're working with mutable strings.
However, note that the object on which you call contains() must be a CharSequence as well (usually a String). If you have a StringBuilder and want to check if it contains something, you must call contains() on the StringBuilder? Actually StringBuilder does not have a contains() method – it's only on String. So you'd need to convert the StringBuilder to a String first, or use indexOf() on the StringBuilder (StringBuilder has indexOf). But if you have a String and want to check if it contains a substring from a StringBuilder, you can pass the StringBuilder directly.
contains() – use indexOf() on it instead.contains().contains() in Stream and Lambda Pipelines
contains() is commonly used in stream filters to keep only elements that contain a certain substring. It works naturally with lambdas: list.stream().filter(s -> s.contains("keyword")). But watch out — if any element in the stream is null, you'll get an NPE before the filter even processes the keyword. Always check for nulls first with filter(Objects::nonNull).
Also, remember that contains() is case-sensitive in streams too. If you need case-insensitive, chain toLowerCase(Locale.ROOT) on the element before calling contains().
contains() caused a silent spike in NPE logs during a data migration.contains() in streams.The Case-Sensitive Payment Filter That Lost Us Customers
contains() expecting 'Failed'.contains() check.contains().- Never assume data case from external systems.
- Always normalise when checking substrings from outside your control.
- Add a unit test with both capitalised and lowercase input strings.
contains()contains(). Add a null guard: str != null && str.contains(sub).System.out.println(). Check if one side has leading/trailing whitespace. Use trim() if needed.Key takeaways
String.contains() is case-sensitive. For case-insensitive contains, use str.toLowerCase(Locale.ROOT).contains(query.toLowerCase(Locale.ROOT)).contains() on any string that might be null.contains() chained with || for simple cases or a stream with anyMatch() for a list of patterns.contains()Common mistakes to avoid
4 patternsCalling contains() on a potentially null string
contains(): str != null && str.contains(sub).Passing null to contains()
contains() cannot be null.Using contains() with case-sensitive expectation on user input
contains().Using contains() in a tight loop on large strings
contains() if the string doesn't change, or use indexOf() with a starting position to search incrementally. For multiple patterns, use Aho-Corasick.Interview Questions on This Topic
How would you implement a case-insensitive substring check in Java?
contains(). The standard approach is to convert both the string and the substring to the same case using toLowerCase(Locale.ROOT) or toUpperCase(Locale.ROOT) before calling contains(). Always use Locale.ROOT to avoid locale-specific case conversion issues such as the Turkish 'i' problem. Example: str.toLowerCase(Locale.ROOT).contains(sub.toLowerCase(Locale.ROOT)).Frequently Asked Questions
That's Strings. Mark it forged?
3 min read · try the examples if you haven't