Java String lastIndexOf: Usage and Edge Cases
Learn how to use Java String lastIndexOf effectively: overloads, backward search behavior, fromIndex handling, edge cases, and performance considerations.
The lastIndexOf method is a core part of the String class for locating the last occurrence of a character or substring. It searches backward from the end of the string (or from a specified index) and returns the index of the last match. This article explains the overloads, how the backward search works, edge cases, and performance implications.
The lastIndexOf Method and Its Overloads
The String class provides four overloads of lastIndexOf:
public int lastIndexOf(int ch) public int lastIndexOf(int ch, int fromIndex) public int lastIndexOf(String str) public int lastIndexOf(String str, int fromIndex)
The first two accept a Unicode code point (int), while the last two accept a String. The two-argument versions specify a starting index from which to search backward. All overloads return the index of the last occurrence, or -1 if no match is found.
Here is a minimal example:
String text = "banana"; int lastA = text.lastIndexOf('a'); // returns 5 int lastN = text.lastIndexOf("an"); // returns 3 System.out.println(lastA); // 5 System.out.println(lastN); // 3
In the first call, lastIndexOf('a') scans from the end and finds the last 'a' at index 5. In the second, it finds the last occurrence of the substring "an" starting at index 3 (the "an" in "nana").
How lastIndexOf Searches Backward
Unlike indexOf, which searches from the beginning, lastIndexOf starts at the end of the string (or at fromIndex) and moves toward index 0. For a character argument, it checks the requested code point or UTF-16 code unit at each position. For a String argument, it checks each potential starting position, moving backward.
Consider this example:
String path = "/usr/local/bin"; int lastSlash = path.lastIndexOf('/'); System.out.println(lastSlash); // 8
The last slash is at index 8, so the method returns 8. This is useful for extracting the final component of a path or file name.
The backward search means the first match found is the one closest to the end of the string (or to fromIndex). This is the opposite of indexOf, which finds the earliest occurrence.
Using the fromIndex Parameter
The two-argument overloads restrict candidate start positions to those at or before fromIndex. The search proceeds from fromIndex down to 0. If fromIndex is greater than or equal to the string length, the entire string is searched. If it is negative, the result is always -1.
String text = "hello hello"; int firstHello = text.lastIndexOf("hello", 5); // only start positions up to 5 System.out.println(firstHello); // 0
Here, fromIndex is 5, so the search considers only start positions 0 through 5. The second "hello" starts at index 6, so it is excluded; the last match in the allowed range is at index 0. Without the limit, lastIndexOf("hello") would return 6.
For a character search, a fromIndex larger than the last valid index is clamped to the last index. For a substring search, it is clamped to the last position from which that substring could start; if the substring is longer than the string, no match is possible and the result is -1.
This is particularly useful when you need to find the last occurrence before a certain point, such as the last directory separator before a given position in a path.
Edge Cases: Empty Strings and Not Found Results
When the search string is empty, lastIndexOf returns fromIndex (or the string length when fromIndex is larger), provided fromIndex is not negative. This is consistent with the definition that an empty string is considered to exist at every index, and the last occurrence is the highest index allowed.
String text = "abc"; System.out.println(text.lastIndexOf("")); // 3 System.out.println(text.lastIndexOf("", 1)); // 1
If the character or substring is not found, the method returns -1. This is the standard sentinel value for "not present" in Java's string search methods.
String text = "abc"; int result = text.lastIndexOf('z'); // -1 System.out.println(result);
When fromIndex is negative, the method returns -1 without performing a search.
Performance and Runtime Behavior
The time complexity of lastIndexOf is O(n * m) in the worst case, where n is the length of the string and m is the length of the search substring. For a single character, it is O(n). The method does not use an advanced string-matching algorithm like KMP; it performs a simple backward scan with character-by-character comparison.
For most practical purposes, this is efficient enough. However, if you repeatedly search for the same substring in a large string, consider whether a different approach, such as a Matcher with a precompiled Pattern, would be more appropriate. The String methods are not optimized for repeated searches or complex patterns.
lastIndexOf does not allocate a new array or copy the string; it works directly on the underlying character array.
Comparing lastIndexOf with indexOf
The choice between indexOf and lastIndexOf depends on which occurrence you need. indexOf returns the first occurrence from the beginning; lastIndexOf returns the last occurrence from the end. The table below summarizes the key differences:
| Method | Search direction | Returns | Typical use case |
|---|---|---|---|
indexOf | Forward | First occurrence | Find first delimiter |
lastIndexOf | Backward | Last occurrence | Find last file separator |
Both methods accept the same overloads and return -1 when no match is found. The choice is driven by the position you need, not by correctness in a general sense.
Common Mistakes and How to Avoid Them
A frequent mistake is forgetting that lastIndexOf returns the starting index of the last substring. For example, "abcabc".lastIndexOf("abc") returns 3, not 6. The substring starts at index 3, even though it ends at index 5.
Another common error is using fromIndex incorrectly. If you want to search the entire string, do not pass a value that is too small. For instance, text.lastIndexOf("a", 2) will not find an 'a' at index 4 because the search is limited to start positions 0..2.
When dealing with Unicode supplementary characters, remember that lastIndexOf(int ch) expects a code point, not a UTF-16 code unit. If you pass one half of a surrogate pair as an int, you are searching for that raw code unit, not the complete character. For example:
String emoji = "😀😀"; int last = emoji.lastIndexOf(0x1F600); // 2, the second code point starts at index 2
This is because the string is stored as UTF-16, and the emoji occupies two code units. The method correctly handles code points when you pass the full code point value.
Finally, be aware that lastIndexOf is case-sensitive. To perform a case-insensitive search, normalize the strings first, for example by converting both to lowercase, or use a Pattern with the CASE_INSENSITIVE flag.
Understanding these details helps you use lastIndexOf correctly in parsing, path manipulation, and text-processing code.