Java String indexOf: Usage and Edge Cases
java string indexof: Learn how Java's String.indexOf() finds characters and substrings, handles missing matches, and behaves across its overloads.
When you look up java string indexof, the core behavior is simple: String.indexOf() searches for the first occurrence of a given character or substring and returns the zero-based position where the match begins. If no match exists, it returns -1. That single rule covers most of the behavior developers need to remember.
What String.indexOf() Actually Returns
The indexOf() method on java.lang.String scans the string from left to right and returns the index of the first match. Indexing starts at zero, so the first character is at position 0, and the space between words counts as a position.
String text = "The quick brown fox"; int position = text.indexOf('q'); System.out.println(position); // 4
The 'q' character sits at index 4 because the space at index 3 is skipped during the scan. The method stops at the first match and does not continue searching.
The Four Overloads
String provides four overloads of indexOf():
| Method signature | Behavior |
|---|---|
int indexOf(int ch) | Finds the first occurrence of a Unicode code point |
int indexOf(int ch, int fromIndex) | Finds the first occurrence at or after fromIndex |
int indexOf(String str) | Finds the first occurrence of a substring |
int indexOf(String str, int fromIndex) | Finds the first substring occurrence at or after fromIndex |
The int parameter in the character overload accepts a Unicode code point, not just a char. This distinction matters for supplementary characters that require two char values in the UTF-16 representation used by Java strings. For characters in the Basic Multilingual Plane, passing a char literal like 'a' works directly because char widens to int.
Searching From a Starting Position
The fromIndex overloads let you skip past earlier matches. This is useful when you need to find the second or third occurrence of a value.
String csv = "apple,banana,apple,cherry"; int first = csv.indexOf("apple"); int second = csv.indexOf("apple", first + 1); System.out.println(first); // 0 System.out.println(second); // 13
The second call starts scanning at index 1, so the first "apple" at index 0 is ignored. The search continues until it finds the next occurrence at index 13. Passing first + 1 is a common pattern for iterating over repeated matches.
Understanding the -1 Return Value
A return value of -1 means the character or substring is absent from the string. This is a sentinel value, not a valid index. Any code that uses the result directly as an array or string index must check for -1 first.
String path = "/var/log/app.log"; int slash = path.indexOf('/'); if (slash != -1) { String directory = path.substring(0, slash); System.out.println(directory); }
Calling substring() with -1 would throw an IndexOutOfBoundsException. The explicit check is not optional when the input is dynamic.
Empty Strings and Edge Cases
indexOf("") returns 0 for any non-null string, because the empty string matches at every position and the first position is index 0. The character overload with fromIndex behaves differently: indexOf('a', fromIndex) returns -1 when fromIndex is greater than the last valid index, but indexOf("", fromIndex) returns fromIndex when fromIndex is within bounds and the string length when fromIndex equals the length.
A NullPointerException is thrown if the String argument is null. The character overload does not accept a null argument because int is a primitive type. These edge cases rarely appear in normal code, but they explain why indexOf("") and indexOf(' ') produce different results for the same string.
Performance Considerations
The substring overload is implemented with a naive search that compares characters from each candidate starting position. In the worst case, a pattern like "aaaaab" searched against a string of repeated 'a' characters approaches O(n * m) comparisons, where n is the string length and m is the pattern length. The character overload is O(n) because each position requires a single comparison.
For repeated searches over the same large string, consider whether a single pass with a Matcher or a Map of positions would avoid redundant work. For typical short strings, the difference is negligible, and indexOf() remains the clearest option.
Choosing Between indexOf and lastIndexOf
lastIndexOf() mirrors indexOf() but scans from the end of the string toward the beginning. It also returns -1 when no match exists. Use it when the last occurrence matters, such as extracting a file extension from a path.
String filename = "report.final.pdf"; int dot = filename.lastIndexOf('.'); String extension = filename.substring(dot + 1); System.out.println(extension); // pdf
The lastIndexOf() overloads accept the same four argument shapes: character, character with fromIndex, string, and string with fromIndex. The fromIndex in lastIndexOf() is the position where the backward search starts, not where it ends.
Practical Pattern: Parsing Repeated Delimiters
A common production pattern is looping over matches with indexOf() to split a string without allocating an array.
String data = "id=42;name=alice;role=admin"; int start = 0; while (true) { int semicolon = data.indexOf(';', start); if (semicolon == -1) { String last = data.substring(start); System.out.println(last); break; } String segment = data.substring(start, semicolon); System.out.println(segment); start = semicolon + 1; }
This avoids the overhead of split() when the delimiter is a single character and the segments must be processed incrementally. The loop terminates because start advances past each semicolon, and the final segment is handled by the -1 branch. This pattern keeps the logic explicit and avoids the regex compilation that split() performs even for simple delimiters.