String.replaceAll значительно медленнее, чем делать работу самостоятельно
У меня есть старый кусок кода, который выполняет поиск и замену токенов в строке.
Получает карту from
а также to
пар, перебирает их, и для каждой из этих пар перебирает целевую строку, ищет from
с помощью indexOf()
и заменяет его значением to
, Он делает всю работу на StringBuffer
и в конце концов возвращает String
,
Я заменил этот код этой строкой: replaceAll("[,. ]*", "");
И я провел несколько сравнительных тестов производительности.
При сравнении для 1,000,000
итерации, я получил это:
Старый код: 1287мс
Новый код: 4605мс
В 3 раза дольше!
Затем я попытался заменить его на 3 звонка replace
: replace(",", "");
replace(".", "");
replace(" ", "");
Это привело к следующим результатам:
Старый код: 1295
Новый код: 3524
В 2 раза дольше!
Любая идея, почему replace
а также replaceAll
так неэффективны? Могу ли я сделать что-нибудь, чтобы сделать это быстрее?
Редактировать: Спасибо за все ответы - главная проблема была в том, что [,. ]*
не сделал то, что хотел. Меняя это, чтобы быть [,. ]+
почти равнялся производительности решения на основе не-Regex. Использование предварительно скомпилированного регулярного выражения помогло, но оказалось незначительным. (Это решение очень применимо для моей проблемы.
Тестовый код:
Замените строку на регулярное выражение: [,. ] *
Замените строку на регулярное выражение: [,. ] +
Замените строку на регулярное выражение: [,. ] + и предварительно скомпилированный шаблон
5 ответов
Хотя использование регулярных выражений оказывает некоторое влияние на производительность, оно не должно быть таким ужасным.
Обратите внимание, что с помощью String.replaceAll()
скомпилирует регулярное выражение каждый раз, когда вы его вызываете.
Вы можете избежать этого, явно используя Pattern
объект:
Pattern p = Pattern.compile("[,. ]+");
// repeat only the following part:
String output = p.matcher(input).replaceAll("");
Обратите внимание, что с помощью +
вместо *
избегает замены пустых строк и, следовательно, может также ускорить процесс.
replace
а также replaceAll
использует регулярные выражения внутри себя, что в большинстве случаев дает серьезное влияние на производительность по сравнению, например, StringUtils.replace(..)
,
String.replaceAll()
:
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this ).replaceAll(
replacement);
}
String.replace()
использует Pattern.compile внизу.
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL)
.matcher(this ).replaceAll(
Matcher.quoteReplacement(replacement.toString()));
}
Также см. Замена всех вхождений подстроки в строке - что более эффективно в Java?
Как я положил в комментарии [,. ]* соответствует пустой строке "". Таким образом, каждый "пробел" между символами соответствует шаблону. Это отмечено только в производительности, потому что вы заменяете много "" на "".
Попробуйте сделать это:
Pattern p = Pattern.compile("[,. ]*");
System.out.println(p.matcher("Hello World").replaceAll("$$$");
Возвращает:
Н $$$ $$$ е л о $$$ $$$$$$ Вт $$$ $$$ о г л $$$ $$$ $$$ д!$$$
Неудивительно, что медленнее делать это "вручную"! Вы должны попробовать с [,. ]+
Когда дело доходит до replaceAll("[,. ]*", "")
это не такой уж большой сюрприз, поскольку он опирается на регулярные выражения. Движок регулярных выражений создает автомат, который запускает вход. Ожидаются некоторые накладные расходы.
Второй подход (replace(",", "")...
) также использует регулярные выражения внутри. Здесь данный шаблон, однако, скомпилирован с использованием Pattern.LITERAL
поэтому накладные расходы регулярного выражения должны быть пренебрежимо малы.) В этом случае это, вероятно, связано с тем, что Strings
являются неизменяемыми (как бы мало вы ни изменяли, вы создадите новую строку) и, следовательно, не так эффективны, как StringBuffers
которые манипулируют строкой на месте.
Это очень старый пост, но, к сведению, начиная с Java 9 характеристики производительности String.replace(String,String) изменились, теперь он не использует шаблон под капотом.
https://medium.com/javarevisited/micro-optimizations-in-java-string-replaceall-c6d0edf2ef6