Подтвердить что ты не робот

String replaceAll() и Matcher replaceAll() (Различия в производительности)

Довольно простой вопрос, но это происходит от человека C/С++, проникающего в тонкости Java.

Я понимаю, что могу запустить jUnit и несколько собственных тестов производительности, чтобы получить ответ; но мне просто интересно, если это там.

Известны ли различия между String.replaceAll() и Matcher.replaceAll() (на объекте Matcher, созданном из Regex.Pattern) с точки зрения производительности?

Кроме того, каковы различия между уровнями API в API высокого уровня? (Неизменяемость, обработка NULL, обработка пустых строк, приготовление кофе и т.д.)

4b9b3361

Ответ 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