Java String endsWith: Syntax, Edge Cases, Performance
java string endswith: Learn how to use Java's String.endsWith() method for suffix checks, including syntax, case sensitivity, edge cases, and performance tradeoffs.
The String.endsWith(String suffix) method in Java checks whether a string ends with a given suffix. It returns true if the string ends with the specified suffix, and false otherwise. The method is part of the java.lang.String class and is widely used for validation, parsing, and filtering logic. When working with java string endswith, the typical usage is straightforward: you call the method on the string you want to test and pass the suffix as an argument.
String filename = "report.pdf"; if (filename.endsWith(".pdf")) { System.out.println("PDF file"); }
The method internally compares the character sequence at the end of the string against the suffix. It does not compile a regular expression or perform any locale-dependent operations, so it is both simple and predictable.
Using endsWith for Suffix Checks
The most common use case is file extension validation, but the method is also useful for URL path checks, configuration key matching, and any scenario where you need to verify a trailing pattern. The syntax is minimal: str.endsWith(suffix) where both are String objects. The method returns a primitive boolean, so it can be used directly in conditional expressions.
String path = "/api/v1/users"; if (path.endsWith("/users")) { // handle user endpoint }
One important detail is that endsWith is case-sensitive. The comparison is performed using the exact character values, so "File.txt".endsWith(".TXT") returns false. This behavior is consistent with most Java string methods, which do not perform case folding unless explicitly requested.
How endsWith Handles Null and Empty Suffixes
If the suffix argument is null, the method throws a NullPointerException. This is a common source of errors when the suffix comes from an external input or a variable that may not have been initialized. You should guard against null suffixes before calling endsWith if there is any chance the value could be null.
String suffix = getSuffix(); // may return null if (suffix != null && filename.endsWith(suffix)) { // safe }
An empty string ("") is a valid suffix, and endsWith("") always returns true for any non-null string. This follows from the definition: every string ends with the empty string. This behavior is consistent with startsWith("") and can be useful in generic algorithms that treat the empty string as a neutral value.
Case Sensitivity and Case-Insensitive Alternatives
Because endsWith is case-sensitive, developers often need a case-insensitive variant. The simplest approach is to normalize both strings to the same case before comparison, for example by calling toLowerCase() or toUpperCase() on both the string and the suffix. However, this creates new string objects and can be problematic with locale-specific characters. A more robust alternative is to use regionMatches with the ignoreCase flag.
The regionMatches method allows you to compare a specific region of a string against another string, optionally ignoring case. To check a suffix, you can compute the start index as str.length() - suffix.length() and call str.regionMatches(true, start, suffix, 0, suffix.length()). This avoids creating new strings and gives you precise control over case handling.
String filename = "Report.PDF"; String suffix = ".pdf"; boolean matches = filename.regionMatches(true, filename.length() - suffix.length(), suffix, 0, suffix.length());
This approach is more verbose but avoids the overhead of lowercasing the entire string and handles Unicode case folding more consistently than toLowerCase() in some locales.
Comparing endsWith with regionMatches and Manual Checks
There are several ways to check a suffix in Java, each with different tradeoffs. The direct endsWith call is the most readable and is optimized internally. It uses a simple loop over the character arrays and does not allocate additional objects. For most applications, this is the right choice.
A manual check using substring and equals is also possible but less efficient because it creates a new string for the substring. For example, filename.substring(filename.length() - suffix.length()).equals(suffix) performs the same logical check but allocates a new String object. This can be wasteful in a loop or on a hot path.
Regular expressions offer a more flexible but heavier alternative. A pattern like ".*\\.pdf$" can match a suffix, but compiling and matching a regex involves significant overhead compared to a simple character comparison. Unless you need pattern features such as alternation or anchors beyond the end, endsWith is almost always preferable.
The following table summarizes the main differences:
| Approach | Case-sensitive | Allocates objects | Performance | Flexibility |
|---|---|---|---|---|
endsWith | Yes | No | Fast | Low |
regionMatches | Configurable | No | Fast | Medium |
substring+equals | Yes | Yes | Slower | Low |
| Regular expression | Configurable | Yes | Slowest | High |
For most suffix checks, endsWith is the best balance of clarity and efficiency. Use regionMatches when you need case-insensitive matching without allocation, and use regex only when the pattern is too complex for a simple suffix.
Performance Considerations for Repeated Suffix Checks
When endsWith is called repeatedly in a loop or on a large number of strings, the performance is generally excellent because the method performs a simple character-by-character comparison from the end of the string. The time complexity is O(n) where n is the length of the suffix, not the length of the entire string, because the method only compares the last suffix.length() characters. This is a subtle but important point: the cost is proportional to the suffix length, not the full string length.
If you are checking many strings against the same suffix, there is no additional setup cost. The method does not cache anything, but it also does not require any preprocessing. For example, filtering a list of filenames for a specific extension is efficient even with thousands of entries.
A common mistake is to convert the string to lowercase before calling endsWith to achieve case-insensitivity. This allocates a new string for every input, which can be expensive in a tight loop. Using regionMatches with ignoreCase avoids that allocation and is the recommended approach when case-insensitive matching is needed at scale.
Another performance consideration is the use of endsWith in a chain of conditions. Because it returns a boolean, you can combine it with other checks using short-circuit operators. For example, if (name != null && name.endsWith(".xml")) avoids a NullPointerException and only calls the method when the string is non-null. This pattern is both safe and efficient.
Edge Cases: Empty Strings and Overlapping Suffixes
An empty string always returns true when used as the suffix, as mentioned earlier. This can lead to subtle bugs if you assume that an empty suffix means "no match". For instance, in a configuration parser, you might want to treat an empty suffix as an invalid condition. In that case, you should check suffix.isEmpty() explicitly before calling endsWith.
Another edge case is when the suffix is longer than the string itself. For example, "abc".endsWith("abcd") returns false because the string does not contain enough characters to match the suffix. The method handles this gracefully without throwing an exception, so you do not need to pre-check lengths.
Overlapping suffixes are not a concern because the method only compares the exact end portion. For example, "abcabc".endsWith("abc") returns true, and "abcabc".endsWith("bc") also returns true. The method does not consider overlapping patterns beyond the exact trailing sequence.
When working with Unicode, endsWith operates on UTF-16 code units, not on code points. This means that supplementary characters (those outside the Basic Multilingual Plane) are represented as surrogate pairs, and the method compares the surrogate pairs as individual char values. For most practical purposes, this is fine, but if you need to match a suffix that ends in the middle of a surrogate pair, the behavior may be unexpected. In such rare cases, you should use regionMatches with appropriate code point handling or work with StringBuilder methods.
When to Use regionMatches Instead of endsWith
The regionMatches method is the more general tool and is useful when you need to check a suffix with case insensitivity or when you want to compare a substring that is not necessarily at the end of the string. For a simple suffix check, endsWith is more readable and less error-prone. However, if you find yourself writing code that calls toLowerCase() on both arguments just to achieve case-insensitive matching, consider switching to regionMatches with the ignoreCase parameter.
Another scenario where regionMatches is preferable is when you need to check a suffix at a specific offset, such as when parsing a token stream. You can use regionMatches to compare a region starting at any index, which gives you more flexibility than endsWith.
Here is an example that checks for a case-insensitive suffix without allocating new strings:
public static boolean endsWithIgnoreCase(String text, String suffix) { return text.regionMatches(true, text.length() - suffix.length(), suffix, 0, suffix.length()); }
This method is safe as long as text and suffix are non-null and suffix.length() is not greater than text.length(). You can add a length check if needed, but regionMatches will return false if the start index is negative or out of bounds, so the method is defensive enough for most use cases.
In summary, endsWith is the right default for suffix checks. Use regionMatches when you need case-insensitive matching or offset-based comparisons, and avoid regex unless you need pattern features that a simple suffix cannot express.