String.indexOf: примеры (JAVA)
String.indexOf(String str, int fromIndex): intОбщее описание метода
Метод String.indexOf ищет первое вхождение заданного символа или подстроки в строке и возвращает индекс начала вхождения. Применяется при поиске позиции символа или шаблона без использования регулярных выражений. Существует несколько перегрузок:
int indexOf(int ch)- поиск символа, представленного как Unicode кодовая точка или значением типаchar.int indexOf(int ch, int fromIndex)- поиск символа, начиная с позицииfromIndex.int indexOf(String str)- поиск подстрокиstr.int indexOf(String str, int fromIndex)- поиск подстроки, начиная с позицииfromIndex.
Возвращаемое значение: индекс первого символа найденного вхождения (0-based). Если искомое не найдено, возвращается -1. При передачи null в параметр String выбрасывается NullPointerException. Поведение параметра fromIndex: если он меньше нуля, поиск начинается с 0; если он больше или равен длине строки, возвращается -1 (для пустой строки возможны особенности при поиске пустой подстроки).
Особенности с Unicode: при поиске по коду точки Unicode (параметр int ch) метод корректно обрабатывает дополнительные символы (supplementary characters) - для кодовых точек выше 0xFFFF формируется пара суррогатов и возвращается индекс первого символа пары при совпадении.
Производительность: в общем случае линейный проход по строке, в худшем случае сложность может быть пропорциональна длине текста умноженной на длину шаблона. В реализации JDK применяются оптимизации, зависящие от версии Java.
Короткие примеры применения
Примеры демонстрируют разные перегрузки и возможные ответы метода.
// Пример 1: поиск символа
String s = "hello";
int i = s.indexOf('e');
System.out.println(i);
1
// Пример 2: поиск символа с fromIndex
String s2 = "banana";
int i2 = s2.indexOf('a', 2);
System.out.println(i2);
3
// Пример 3: поиск подстроки
String s3 = "abracadabra";
int i3 = s3.indexOf("cada");
System.out.println(i3);
4
// Пример 4: не найдено
String s4 = "test";
int i4 = s4.indexOf("z");
System.out.println(i4);
-1
// Пример 5: поиск supplementary code point
String s5 = "A\uD83D\uDE00B"; // смайлик U+1F600 суррогатная пара
int i5 = s5.indexOf(0x1F600);
System.out.println(i5);
1
Аналоги в Java и их отличия
- lastIndexOf - возвращает индекс последнего вхождения; используется при необходимости найти правое вхождение.
- contains(CharSequence) - возвращает boolean, удобно при проверке наличия, когда индекс не нужен.
- startsWith / endsWith - проверка на префикс или суффикс; быстрее и семантически точнее при соответствующей задаче.
- matches / Pattern / Matcher - регулярные выражения для более сложных шаблонов; предпочтительны при гибком поиске.
- regionMatches - сравнение участка строки с другой строкой без выделения подстрок; полезно для избежания выделения памяти при частых сравнениях.
Выбор между этими методами зависит от цели: если нужен индекс первого вхождения - indexOf, для последнего - lastIndexOf, для простого наличия - contains, для сложных паттернов - регулярные выражения.
Эквиваленты в других языках
Краткие примеры и отличия для популярных языков.
JavaScript
const s = 'hello';
console.log(s.indexOf('l'));
2
Особенности: indexOf возвращает -1 если не найдено; есть includes, возвращающий boolean.
Python
s = 'hello'
print(s.find('l'))
# или
print(s.index('l'))
2 2
find возвращает -1 при отсутствии, index бросает ValueError.
PHP
$s = 'hello';
echo strpos($s, 'l');
2
strpos возвращает false при отсутствии, надо отличать от 0; для многобайтовых строк используется mb_strpos.
SQL (MySQL)
SELECT INSTR('hello', 'l');
3
INSTR возвращает позицию 1-based в MySQL. В стандарте SQL есть POSITION, в SQL Server - CHARINDEX.
C#
string s = "hello";
Console.WriteLine(s.IndexOf('l'));
2
Похожее поведение; есть варианты с учетом культуры и сравнения строк.
Go
import "fmt"
import "strings"
func main() {
fmt.Println(strings.Index("hello", "l"))
}
2
strings.Index работает с байтами UTF-8, для поиска по рунe есть IndexRune.
Lua
print(string.find('hello', 'l')) -- возвращает позицию 1-based
2 2
Kotlin
val s = "hello"
println(s.indexOf('l'))
2
Kotlin использует JVM-реализацию String, поведение совпадает с Java.
Типичные ошибки при использовании
- NullPointerException: вызов
indexOf((String) null)- пример:String s = "a"; s.indexOf(null);Exception in thread "main" java.lang.NullPointerException ... - Ошибка при сравнении с false-похожими значениями: в PHP функция может вернуть
0и одновременноfalse, поэтому требуется строгое сравнение. В Java тип int позволяет избежать этой проблемы. - Неправильная обработка -1: ожидание исключения при ненайденном вхождении вместо проверки на -1.
String s = "abc"; int i = s.indexOf('z'); System.out.println(s.substring(i));Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin -1, end 3 ... - Непонимание поведения fromIndex: если fromIndex < 0, поиск начинается с 0; если fromIndex >= length, возвращается -1.
- Unicode-замешательство: ожидание поиска по графемам вместо кодовых точек может привести к неверным результатам при комбинирующих символах или сложных emoji.
Изменения и эволюция метода
Сам API метода String.indexOf стабилен и не претерпевал семантических изменений в недавних версиях Java. Внутренняя реализация могла меняться для улучшения производительности: в разных версиях JDK применялись оптимизации поиска подстроки и эффективные методы работы с UTF-16. Для специфичных улучшений стоит смотреть заметки о релизах конкретной версии JDK.
Расширенные и нестандартные примеры
Несколько более сложных ситуаций и приёмов с объяснениями.
// 1) Поиск всех вхождений подстроки
String s = "ababa";
String pat = "aba";
int idx = s.indexOf(pat);
while (idx != -1) {
System.out.println("Found at: " + idx);
idx = s.indexOf(pat, idx + 1);
}
Found at: 0 Found at: 2
Пояснение: поиск с сдвигом на 1 позволяет найти перекрывающиеся вхождения.
// 2) Поиск n-го вхождения
String text = "one two one two one";
String token = "one";
int n = 2;
int pos = -1;
int from = 0;
for (int k = 0; k < n; k++) {
pos = text.indexOf(token, from);
if (pos == -1) break;
from = pos + 1;
}
System.out.println(pos);
12
Пояснение: цикл сдвигает откуда начинается следующий поиск, возвращается индекс второго вхождения.
// 3) Регистронезависимый поиск без regex
String a = "Hello World";
int p = a.toLowerCase().indexOf("world");
System.out.println(p);
6
Пояснение: преобразование к одному регистру решает задачу при работе с ASCII и простыми локалями; для сложной локали лучше использовать Collator или Pattern с режимом CASE_INSENSITIVE.
// 4) Поиск supplementary символа и разбор результата
String u = "X\uD83D\uDE00Y"; // U+1F600
int pos = u.indexOf(0x1F600);
System.out.println(pos);
System.out.println(u.charAt(pos));
System.out.println(Integer.toHexString(u.codePointAt(pos)));
1 \uD83D 1f600
Пояснение: indexOf нашел суррогатную пару и вернул индекс первого char. Для получения полной кодовой точки используется codePointAt.
// 5) Быстрый поиск без выделения подстрок при частых запросах
String hay = "..."; // очень большая строка
// вместо hay.substring(i, i+len).equals(pat) использовать regionMatches
if (hay.regionMatches(i, pat, 0, pat.length())) {
// совпадение найдено без выделения новой строки
}
(нет текстового вывода, пример показывает подход)
// 6) Поиск и замена первого вхождения без regex
String original = "foo bar foo";
int posF = original.indexOf("foo");
String result = (posF == -1) ? original : (original.substring(0, posF) + "baz" + original.substring(posF + "foo".length()));
System.out.println(result);
baz bar foo
Пояснение: комбинирование indexOf и substring позволяет реализовать замену первого вхождения без использования replaceFirst.
// 7) Использование с StringBuilder / StringBuffer
StringBuilder sb = new StringBuilder("abcabc");
int posSb = sb.indexOf("bc");
System.out.println(posSb);
1
Пояснение: у StringBuilder и StringBuffer также есть метод indexOf с аналогичным поведением.