Самый быстрый способ выполнить много замен строк в Java
Я должен написать какой-то синтаксический анализатор, который получает строку и заменяет определенные наборы символов другими. Код выглядит так:
noHTMLString = noHTMLString.replaceAll("</p>", "\n");
noHTMLString = noHTMLString.replaceAll("<br/>", "\n\n");
noHTMLString = noHTMLString.replaceAll("<br />", "\n\n");
//here goes A LOT of lines like these ones
Функция очень длинная и выполняет множество замен строк. Проблема здесь в том, что это занимает много времени, потому что метод, который он вызывал много раз, замедляет производительность приложения.
Я читал здесь некоторые темы об использовании StringBuilder в качестве альтернативы, но в нем отсутствует метод ReplaceAll и, как отмечается здесь, страдает ли производительность string.replaceAll() от неизменности строк? метод replaceAll в классе String работает с
Match Pattern & Matcher и Matcher.replaceAll() используют StringBuilder для хранения полученного в итоге значения, поэтому я не знаю, действительно ли переключение на StringBuilder сократит время выполнения подстановок.
Знаете ли вы быстрый способ сделать большую замену строк в быстром способе? Есть ли у вас какие-либо советы по этой проблеме?
Благодарю.
РЕДАКТИРОВАТЬ: я должен создать отчет, который имеет несколько полей с HTML-текстом. Для каждой строки я вызываю метод, который заменяет все html-теги и специальные символы внутри этих строк. С полным отчетом требуется более 3 минут, чтобы разобрать весь текст. Проблема в том, что я должен вызывать метод очень часто
4 ответа
Я обнаружил, что org.apache.commons.lang.StringUtils - самый быстрый, если вы не хотите беспокоиться о StringBuffer.
Вы можете использовать это так:noHTMLString = StringUtils.replace(noHTMLString, "</p>", "\n");
Я провел тестирование производительности, которое оказалось намного быстрее, чем мое собственное решение StrinBuffer, подобное предложенному @extraneon.
Похоже, что вы там разбираете HTML, а не хотите ли вы использовать стороннюю библиотеку вместо того, чтобы заново изобретать колесо?
Я согласен с Martijn в использовании готового решения вместо того, чтобы разбирать его самостоятельно - в пакете javax.xml есть множество вещей, встроенных в Java. Оптимальным решением было бы использовать XSLT-преобразование для замены, это выглядит как идеальный вариант использования. Однако это сложно.
Чтобы ответить на вопрос, рассматривали ли вы возможность использования библиотек регулярных выражений? Похоже, у вас есть много разных вещей, которые вы хотите сопоставить, и замените их одной и той же вещью (\n или пустая строка). Используя регулярные выражения, вы можете быть выражением вроде "<br>|<br/>|<br />"
или даже более умный, как <br.*?>"
создать объект соответствия, для которого вы можете вызвать replaceAll.
Я полностью согласен с Мартийном здесь. Выберите правильный инструмент для работы.
Если ваш файл не HTML, но содержит только некоторые токены HTML, есть несколько способов ускорить процесс.
Во-первых, если некоторое количество входных данных не содержит заменяемых элементов, подумайте о том, чтобы начать с чего-то вроде:
if (!input.contains('<')) {
return input;
}
Во-вторых, рассмотрим регулярное выражение:
Pattern p = Pattern.compile( your_regex );
Не создавайте шаблон для каждой отдельной строки replaceAll, но попробуйте объединить их (в регулярном выражении есть оператор ИЛИ) и пусть Pattern оптимизирует регулярное выражение. Используйте скомпилированный шаблон и не компилируйте его при каждом вызове, это довольно дорого.
Если регулярные выражения немного сложны, вы также можете самостоятельно реализовать более быстрый (но потенциально менее читаемый) механизм замены:
StringBuilder result = new StringBuilder(input.length();
for (int i=0; i < input.length(); i++) {
char c = input.charAt(i);
if ( c != '<' ) {
continue;
}
int closePos = input.indexOf( '>', i);
if (closePos == -1) {// not found
result.append( input.substring(i, input.length());
return result.toString();
}
i = closePos;
String token = input.substring(i, closePos);
if ( token.equals( "p/" ) {
result.append("\\n");
} else if (token.equals(...)) {
} else if (...) {
}
}
return result.toString();
Это может иметь некоторые ошибки:)
Преимущество заключается в том, что вы должны перебирать ввод только один раз. Большой недостаток в том, что это не так просто понять. Вы также можете написать конечный автомат, анализируя для каждого персонажа, каким должно быть новое состояние, и это, вероятно, будет быстрее и еще больше работы.