String.indexOf: примеры (JAVA)

Метод indexOf класса String в Java
Раздел: Строки (String) - поиск и сравнение
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.

Расширенные и нестандартные примеры

Несколько более сложных ситуаций и приёмов с объяснениями.

Пример java
// 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 позволяет найти перекрывающиеся вхождения.

Пример java
// 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

Пояснение: цикл сдвигает откуда начинается следующий поиск, возвращается индекс второго вхождения.

Пример java
// 3) Регистронезависимый поиск без regex
String a = "Hello World";
int p = a.toLowerCase().indexOf("world");
System.out.println(p);
6

Пояснение: преобразование к одному регистру решает задачу при работе с ASCII и простыми локалями; для сложной локали лучше использовать Collator или Pattern с режимом CASE_INSENSITIVE.

Пример java
// 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.

Пример java
// 5) Быстрый поиск без выделения подстрок при частых запросах
String hay = "..."; // очень большая строка
// вместо hay.substring(i, i+len).equals(pat) использовать regionMatches
if (hay.regionMatches(i, pat, 0, pat.length())) {
  // совпадение найдено без выделения новой строки
}
(нет текстового вывода, пример показывает подход)
Пример java
// 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.

Пример java
// 7) Использование с StringBuilder / StringBuffer
StringBuilder sb = new StringBuilder("abcabc");
int posSb = sb.indexOf("bc");
System.out.println(posSb);
1

Пояснение: у StringBuilder и StringBuffer также есть метод indexOf с аналогичным поведением.

джава String.indexOf function comments

En
String.indexOf Returns the index within this string of the first occurrence of the specified substring