indexOf()
Finds the index of the first match of a char or substring, from the start or a given position, and returns -1 when there is none.
public int indexOf(String str, int fromIndex)- Added in Java Java 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$str | String | The text to find. An int overload, indexOf(int ch), searches for a single char or code point. |
$fromIndexoptional | int= 0 | Where to start searching. Negative values act as 0; values past the end find nothing. |
Return value
intThe index of the first occurrence at or after fromIndex, or -1 if there is none.
Errors
NullPointerException if str is null. The Java 21 overloads that take beginIndex and endIndex throw StringIndexOutOfBoundsException for an invalid range.
Changelog
- PHP Java 21Added indexOf(int ch, int beginIndex, int endIndex) and indexOf(String str, int beginIndex, int endIndex), which search only inside a range.
indexOf returns the position of the first match, or -1 when there is none. It comes in two families: one takes an int (a char or a full code point), the other a String. Both accept an optional start index, and lastIndexOf searches backwards from the end.
"[email protected]".indexOf('@') // 3
"[email protected]".indexOf("example") // 4
"[email protected]".lastIndexOf('.') // 11
"[email protected]".indexOf('#') // -1Always handle -1
The bug is rarely in the search; it is in what happens next. s.substring(s.indexOf(':') + 1) returns the whole String when there is no colon, because -1 + 1 is 0. Branch on -1 explicitly and decide what missing means for your input: reject the request, use a default, or skip the line.
Searching from a position
indexOf(x, from) starts at from. It is forgiving: a negative start is treated as 0 and a start past the end just returns -1. That makes a loop that finds every occurrence short: search, use the hit, then search again from just after it.
Java 21 added range overloads, indexOf(ch, beginIndex, endIndex) and indexOf(str, beginIndex, endIndex). They search only inside the range, which saves cutting a substring first, and unlike the older overloads they throw StringIndexOutOfBoundsException if the range is invalid.
indexOf, contains or a regex?
Use contains when you only need yes or no, since it says what you mean. Use indexOf when you need the position to cut around it. Use a regular expression when the thing you are looking for has a shape rather than fixed text. For URLs, prefer java.net.URI over hand-made index arithmetic, because it handles ports, user info, query strings and escaping correctly.
The search is case-sensitive and compares chars exactly. If case should not matter, do not take an index from a lowercased copy and use it on the original: case mapping can change the length, as the toUpperCase() lesson shows. Use regionMatches(true, ...) at a known position, or a Pattern compiled with CASE_INSENSITIVE | UNICODE_CASE and read matcher.start().
Example
When to use it
- Locating the '=' in each query parameter or .env line to split key from value.
- Finding the last '.' of an uploaded file name to read its extension.
- Scanning a route template for {placeholders} when building a small router.
Frequently asked questions
What does indexOf return if the text is not found?
-1. Every overload of indexOf and lastIndexOf uses -1 for no match, so compare with < 0 or == -1 before using the result as an index.
How do I find all occurrences of a substring in Java?
Loop: call indexOf(str, from), use the hit, set from to hit + str.length() (or hit + 1 to allow overlaps), and stop when the result is -1. For patterns, Matcher.find() or Matcher.results() does the same with a regular expression.
More examples
Finding every placeholder in a route template
Each search starts just after the previous match, so the loop visits every placeholder once and stops when indexOf returns -1. This is the core of a tiny path-template router.
void main() {
String template = "/users/{id}/orders/{orderId}";
int from = 0;
while (true) {
int open = template.indexOf('{', from);
if (open < 0) break;
int close = template.indexOf('}', open);
IO.println(template.substring(open + 1, close) + " at " + open);
from = close + 1;
}
}
// Output:
// id at 7
// orderId at 19Searching inside a range (Java 21)
The range overload limits the search to the token before the semicolon without creating a substring. Note the different contracts: the older fromIndex overload silently returns -1 for a start past the end, while the Java 21 overload throws.
void main() {
String header = "Bearer abc.def.ghi; Path=/";
int end = header.indexOf(';');
IO.println(header.indexOf('.', 7, end));
IO.println(header.indexOf('=', 0, end)); // '=' only appears after end
IO.println(header.indexOf('='));
IO.println(header.indexOf('a', 100)); // old overload: no exception
try {
header.indexOf('a', 5, 100); // range overload: checked
} catch (StringIndexOutOfBoundsException e) {
IO.println(e.getMessage());
}
}
// Output:
// 10
// -1
// 24
// -1
// Range [5, 100) out of bounds for length 26Pulling the host out of a URL
The hand-written version shows the indexOf and substring pattern, and also its limits: it keeps the port and would mishandle user info or a query string. java.net.URI parses the full syntax, so use it for real URLs.
String host(String url) {
int scheme = url.indexOf("://");
int start = scheme < 0 ? 0 : scheme + 3;
int slash = url.indexOf('/', start);
return slash < 0 ? url.substring(start) : url.substring(start, slash);
}
void main() {
IO.println(host("https://api.example.com/v1/users"));
IO.println(host("http://localhost:8080"));
IO.println(host("example.org/path"));
IO.println(URI.create("https://api.example.com/v1/users").getHost());
}
// Output:
// api.example.com
// localhost:8080
// example.org
// api.example.com
Discussion