Im работает над расширением HttpServlet (плагином) для CMS, и его задачей является фильтрация ответа HTML.
В моем методе filterResponse
я получаю запрошенный text/html
в String, который является одним из трех параметров.
В частности, мне нужно выполнить поиск в html String для определенного шаблона URL и немного изменить его. Теперь это может быть тяжелая операция, когда в большом документе есть много ссылок, и я хочу как можно больше оптимизировать этот процесс.
Моя первая версия просто использовала метод replace("://www.", "://www-x1.")
В String.
Затем в текущей версии я использую классы Matcher
и Pattern
. Пример кода:
@Override
public String filterResponse(HttpServletRequest request, String textHtml, String contentType) throws Exception {
Pattern pattern = Pattern.compile("://www.");
Matcher m = pattern.matcher(textHtml);
String response = m.replaceAll("://www-x1.");
.....
.....
return response;
}
В коде реального мира я предварительно компилирую шаблоны (Pattern) в статических полях и делаю то же самое со строками, которые сопоставляются.
Любые идеи о том, как сделать это быстрее? Возможно, конвертировать в объект DOM или какой-либо другой объект XML для ускорения запросов? И т.п.
Прежде всего, вы используете сопоставление шаблонов, где у вас нет шаблона. Это не только расточительствование производительности, это ошибка. Для "://www."
используемый в качестве образца, последняя точка соответствует любому символу.
Если вы хотите заменить простую String
а не шаблон, вы можете использовать String.replace
вместо String.replaceAll
. Это исправляет ошибку и сохраняет все накладные расходы на создание Pattern
и Matcher
внутри.
Если вы хотите использовать Pattern.compile("://www.", Pattern.LITERAL)
регулярных выражений, самым простым Pattern.compile("://www.", Pattern.LITERAL)
является использование Pattern.compile("://www.", Pattern.LITERAL)
чтобы прекратить интерпретацию специальных символов и разрешить использование алгоритма Boyer-Moore для всей последовательности. Это, однако, только окупится, если вы сможете сохранить и повторно использовать подготовленный шаблон.
Кроме того, метод replaceAll
предлагает функцию интерпретации обратных ссылок на группы совпадений в заменяющей String
. Поскольку вы не используете эту функцию, вы можете сохранить накладные расходы, связанные с этой функцией, путем реализации цикла замены самостоятельно. В качестве бонуса вы можете использовать StringBuilder
где Matcher
использует StringBuffer
, но это лишь незначительное улучшение.
static String replace(String source)
{// the pattern would be better off being re-usable stored in a static field
final Pattern pattern = Pattern.compile("://www.", Pattern.LITERAL);
final Matcher m = pattern.matcher(source);
boolean result = m.find();
if (result) {
StringBuilder sb = new StringBuilder(source.length()+16);
int p=0;
do {
sb.append(source, p, m.start()).append("://www-x1.");
p=m.end();
} while (m.find());
sb.append(source, p, source.length());
return sb.toString();
}
return source;
}
Но, как уже говорилось, использование этого простого выражения не может быть лучшим выбором для этого простого случая.
replace
а не replaceAll
в своем вопросе. Но знали ли вы о разнице? Переход от replace
к регулярному выражению не является естественным выбором ...
replace
строк? Или разница маргинальная? Я объявляю объекты Pattern как статические конечные поля.