String replaceAll() и Matcher replaceAll() (Различия в производительности)
Довольно простой вопрос, но это происходит от человека C/С++, проникающего в тонкости Java.
Я понимаю, что могу запустить jUnit и несколько собственных тестов производительности, чтобы получить ответ; но мне просто интересно, если это там.
Известны ли различия между String.replaceAll() и Matcher.replaceAll() (на объекте Matcher, созданном из Regex.Pattern) с точки зрения производительности?
Кроме того, каковы различия между уровнями API в API высокого уровня? (Неизменяемость, обработка NULL, обработка пустых строк, приготовление кофе и т.д.)
Ответы
Ответ 1
В соответствии с документацией для String.replaceAll
, он должен сказать следующее о вызове метода:
Вызов этого метода форма str.replaceAll(regex, repl)
дает тот же результат, что и Выражение
Pattern.compile(regex).matcher(str).replaceAll(repl)
Следовательно, можно ожидать производительности между вызовом String.replaceAll
и явным образом созданием Matcher
и Pattern
должно быть одинаковым.
Edit
Как отмечалось в комментариях, разница в производительности не существует для одного вызова replaceAll
из String
или Matcher
, однако, если нужно выполнить несколько вызовов replaceAll
, можно было бы ожидать, что было бы выгодно удерживать скомпилированный Pattern
, поэтому относительно дорогостоящая компиляция шаблона регулярных выражений не должна выполняться каждый раз.
Ответ 2
Исходный код String.replaceAll()
:
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
Сначала он должен скомпилировать шаблон - если вы собираетесь запускать его много раз с тем же шаблоном на коротких строках, производительность будет намного лучше, если вы повторно используете один скомпилированный шаблон.
Ответ 3
Основное отличие состоит в том, что если вы удерживаете Pattern
, используемый для создания Matcher
, вы можете избежать повторной компиляции регулярного выражения каждый раз, когда вы его используете. Пройдя через String
, вы не получаете возможность "кэшировать", как это.
Если у вас есть другое регулярное выражение каждый раз, использование класса String
replaceAll
в порядке. Если вы применяете одно и то же регулярное выражение ко многим строкам, создайте один Pattern
и повторно используйте его.
Ответ 4
Неизменяемость/безопасность потоков: скомпилированные шаблоны неизменны, совпадений нет. (см. Является ли Java Regex Thread безопасным?)
Обработка пустых строк: replaceAll должно обрабатывать пустые строки изящно (он не будет соответствовать пустым строкам входных строк)
Изготовление кофе и т.д.: последний раз я слышал, ни для String, ни для Pattern, ни для Matcher не было никаких функций API для этого.
edit: как и для обработки NULL, документация для String и Pattern явно не говорит об этом, но я подозреваю, что они выбрали исключение NullPointerException, поскольку они ожидают String.
Ответ 5
Реализация String.replaceAll
сообщает вам все, что вам нужно знать:
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
(И документы говорят то же самое.)
Пока я не проверял кеширование, я бы наверняка ожидал, что компиляция шаблона один раз и сохранение статической ссылки на это будет более эффективной, чем вызов Pattern.compile
с тем же шаблоном каждый раз. Если в кэше будет небольшая экономия - если нет, это может быть большой.
Ответ 6
Разница в том, что String.replaceAll() компилирует регулярное выражение каждый раз, когда он вызывает. Нет эквивалента для .NET static Regex.Replace() метода, который автоматически кэширует скомпилированное регулярное выражение. Обычно replaceAll() - это то, что вы делаете только один раз, но если вы собираетесь называть его повторно с тем же самым регулярным выражением, особенно в цикле, вы должны создать объект Pattern и использовать метод Matcher.
Вы также можете создать Матчи и использовать его метод reset(), чтобы перенастроить его для каждого использования:
Matcher m = Pattern.compile(regex).matcher("");
for (String s : targets)
{
System.out.println(m.reset(s).replaceAll(repl));
}
Эффективное преимущество повторного использования Matcher, конечно же, не так велико, как повторное использование шаблона.
Ответ 7
Другие ответы достаточно охватывают рабочую часть OP, но другая разница между Matcher::replaceAll
и String::replaceAll
также является причиной для компиляции вашего собственного Pattern
. Когда вы компилируете Pattern
самостоятельно, существуют опции, такие как флаги, для изменения способа применения регулярного выражения. Например:
Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE);
Matcher
будет применять все флаги, которые вы устанавливаете при вызове Matcher::replaceAll
.
Существуют и другие флаги, которые вы можете установить. В основном я просто хотел указать, что API Pattern
и Matcher
имеет множество опций и что основная причина выйти за рамки простого String::replaceAll