Find: примеры (JAVA)
find: booleanОбщее описание
В Java под именем find чаще понимается не одна функция, а несколько методов в разных API, предназначенных для поиска: поиск совпадений регулярного выражения (java.util.regex.Matcher.find()), поиск файлов в дереве каталогов (java.nio.file.Files.find()), а также операции потока, возвращающие найденный элемент (java.util.stream.Stream.findFirst() и findAny()). Ниже приведено краткое, но подробное описание наиболее распространённых вариантов.
java.util.regex.Matcher.find()
Сигнатура: boolean find() и boolean find(int start).
Назначение
Ищет следующее (или первое при вызове в начале) совпадение шаблона в входной строке. При успешном нахождении соответствующие методы group(), start(), end() возвращают данные о совпадении.
Аргументы
int start- опциональный индекс в исходной строке, с которого начинается поиск.
Возвращаемое значение
true при нахождении совпадения, false при отсутствии.
java.nio.file.Files.find()
Сигнатура: Stream.
Назначение
Производит обход дерева файлов, начиная с start, до глубины maxDepth, и возвращает поток путей, удовлетворяющих предикату matcher. Рекомендуется оборачивать результат в try-with-resources для закрытия потока.
Аргументы
Path start- корневой каталог для поиска.int maxDepth- максимальная глубина обхода (0 - только сам каталог).BiPredicate<Path,BasicFileAttributes> matcher- предикат для фильтрации путей по атрибутам.FileVisitOption... options- опции обхода (например, FOLLOW_LINKS).
Возвращаемое значение
Поток Stream<Path>. При ошибках ввода-вывода исключение выбрасывается при попытке терминальной операции.
java.util.stream.Stream.findFirst()/findAny()
Сигнатуры: Optional<T> findFirst(), Optional<T> findAny().
Назначение
Выполняет терминальную операцию поиска элемента: findFirst возвращает первый элемент потока в порядке источника; findAny разрешает возвращать любой элемент, особенно полезен в параллельных потоках для повышения производительности.
Аргументы
Аргументы отсутствуют. Часто применяется после операций фильтрации.
Возвращаемое значение
Optional<T>, пустой если элемент не найден.
Короткие примеры
1) Matcher.find - извлечение всех вхождений
import java.util.regex.*;
Pattern p = Pattern.compile("\\b\\w+\\b");
Matcher m = p.matcher("one two 123");
while (m.find()) {
System.out.println(m.group() + " (" + m.start() + "," + m.end() + ")");
}
one (0,3) two (4,7) 123 (8,11)
2) Stream.findFirst - поиск первого удовлетворяющего элемента
import java.util.*;
Optional<Integer> r = Arrays.asList(5, 3, 8, 1, 9).stream()
.filter(x -> x > 4)
.findFirst();
System.out.println(r);
Optional[5]
3) Files.find - поиск файлов с расширением .java (короткий вывод)
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;
try (var s = Files.find(Paths.get("src"), 5,
(path, attr) -> attr.isRegularFile() && path.toString().endsWith(".java"))) {
s.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
src/com/example/Main.java src/com/example/util/Helper.java ... (список файлов .java)
Похожие методы в Java
- String.indexOf() - поиск подстроки по строковым индексам; предпочтителен для простого поиска подстроки без шаблонов и с низкой нагрузкой.
- Pattern.matches() - проверка полного соответствия всей строки шаблону; отличается от
Matcher.find(), который ищет подстроки. - List.indexOf() - поиск позиции элемента в списке (равенство через
equals); применяется при необходимости индекса, а не объекта-обёрткиOptional. - Stream.filter(...).findFirst() - комбинация фильтрации и поиска; если нужен конкретный найденный элемент, эта пара часто удобнее чем ручной цикл.
Выбор зависит от задачи: для шаблонного поиска - Matcher.find(), для поиска по файловой структуре - Files.find(), для поиска элементов коллекций - операции Stream или методы коллекций.
Аналоги в других языках
PHP
// preg_match_all
$subject = "one two 123";
preg_match_all('/\\b\\w+\\b/', $subject, $m);
print_r($m[0]);
Array
(
[0] => one
[1] => two
[2] => 123
)
JavaScript
// RegExp.prototype.exec и String.indexOf
const str = 'one two 123';
const re = /\b\w+\b/g;
let m;
while ((m = re.exec(str)) !== null) {
console.log(m[0], m.index);
}
// String.prototype.indexOf
console.log(str.indexOf('two'));
one 0 two 4 123 8 4
Python
import re
s = 'one two 123'
for m in re.finditer(r'\b\w+\b', s):
print(m.group(), m.start(), m.end())
# list.index
print([5,3,8].index(3))
one 0 3 two 4 7 123 8 11 1
SQL
-- Поиск строк с LIKE
SELECT * FROM users WHERE name LIKE '%Ivan%';
(записи, в которых имя содержит 'Ivan')
C#
using System.Text.RegularExpressions;
var m = Regex.Match("one two 123", "\\b\\w+\\b");
while (m.Success) {
Console.WriteLine($"{m.Value} ({m.Index},{m.Index + m.Length})");
m = m.NextMatch();
}
one (0,3) two (4,7) 123 (8,11)
Go
import (
"fmt"
"regexp"
)
r := regexp.MustCompile(`\\b\\w+\\b`)
fmt.Println(r.FindAllString("one two 123", -1))
[one two 123]
Отличия от Java-запросов: синтаксис регулярных выражений близок, но управление потоками и доступ к файловой системе различается по API и по поведению параллелизма. В языках со встроенными функциями поиска (Python, JS) код короче, тогда как в Java для безопасности и масштабируемости часто требуется явное закрытие ресурсов и работа с Optional.
Типичные ошибки и примеры
1) Ошибка: ожидание полного совпадения при использовании find()
import java.util.regex.*;
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("abc123");
System.out.println(m.matches()); // ожидают true, но...
false
Объяснение: matches() проверяет всю строку; для поиска подстроки требуется find().
2) Не закрыт поток от Files.find()
import java.nio.file.*;
Stream<Path> s = Files.find(Paths.get("."), 2, (p,a) -> true);
// пропущен try-with-resources
s.forEach(System.out::println);
(работает, но ресурс не закрыт - возможны утечки дескрипторов)
3) Неправильное использование findAny в последовательном потоке при ожидании предсказуемого элемента
Optional<Integer> any = Arrays.asList(1,2,3).stream().findAny();
System.out.println(any);
Optional[1]
Примечание: в последовательном потоке результат детерминирован, но в параллельном findAny может вернуть другой элемент.
4) NullPointerException при использовании Optional без проверки
Optional<String> o = Optional.empty();
String s = o.get(); // исключение
Exception in thread "main" java.util.NoSuchElementException: No value present
Изменения в последних версиях Java
Интерфейсы Matcher.find(), Files.find() и методы Stream API (findFirst, findAny) существуют давно (в основном с Java 8 и ранее). В недавних релизах не было кардинальных изменений в их сигнатурах. Основные эволюционные изменения в экосистеме касаются улучшений производительности JVM и оптимизаций Stream API в реализации, а также появлению новых утилит в сопутствующих пакетах (например, дополнительные методы в классе Pattern/Matcher в более новых релизах). Следует внимательно читать примечания к релизам JVM для деталей о производительности и поведении параллельных потоков.
Расширенные и нестандартные примеры
1) Выбор N-го совпадения с помощью Matcher
import java.util.regex.*;
Pattern p = Pattern.compile("\\b\\w+\\b");
Matcher m = p.matcher("a b c d e");
int target = 3; // третье совпадение (1-based)
int count = 0;
while (m.find()) {
count++;
if (count == target) {
System.out.println("Match: " + m.group());
break;
}
}
if (count < target) System.out.println("Не найдено");
Match: c
2) Files.find с комплексным предикатом (по размеру и расширению) и безопасным закрытием
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;
try (var s = Files.find(Paths.get("."), 4,
(path, attr) -> attr.isRegularFile()
&& path.toString().endsWith(".log")
&& attr.size() > 1024)) {
s.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
./logs/app.log ./archive/old.log ... (файлы .log больше 1 KB)
3) Параллельный Stream и findAny для производительности
import java.util.*;
Optional<Integer> any = Arrays.asList(1,2,3,4,5,6,7,8,9,10)
.parallelStream()
.filter(x -> heavyCheck(x))
.findAny();
System.out.println(any);
static boolean heavyCheck(int x) {
try { Thread.sleep(50); } catch (InterruptedException e) {}
return x % 7 == 0;
}
Optional[7] // может отличаться при других запусках в параллели
4) Извлечение имен и позиций совпадений с группами и последующая подстановка
import java.util.regex.*;
String s = "John: 25, Mary: 30";
Pattern p = Pattern.compile("(\\w+):\\s*(\\d+)");
Matcher m = p.matcher(s);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String name = m.group(1);
String age = m.group(2);
m.appendReplacement(sb, name + "(" + age + ")");
}
m.appendTail(sb);
System.out.println(sb.toString());
John(25), Mary(30)
5) Комбинация Files.find и Stream API для поиска и ранней остановки через findFirst
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;
try (var s = Files.find(Paths.get("."), 10,
(p, a) -> a.isRegularFile() && p.toString().endsWith(".properties"))) {
Optional<Path> any = s.filter(p -> p.getFileName().toString().contains("app"))
.findFirst();
System.out.println(any);
} catch (IOException e) {
e.printStackTrace();
}
Optional[./config/app.properties] // или Optional.empty()
6) Поиск с учётом локали и сложных шаблонов (lookahead/lookbehind)
import java.util.regex.*;
String s = "price: $12.50, tax: $1.25";
Pattern p = Pattern.compile("(?<=\\$)\\d+\\.\\d{2}");
Matcher m = p.matcher(s);
while (m.find()) System.out.println(m.group());
12.50 1.25
В продвинутых сценариях рекомендуется сочетать возможности API: регулярные выражения для сложных текстовых матчей, Files.find для файловой фильтрации и Stream.findFirst/findAny для управляемого извлечения элементов и оптимизации производительности.