Самый быстрый способ выполнить много замен строк в 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();

Это может иметь некоторые ошибки:)

Преимущество заключается в том, что вы должны перебирать ввод только один раз. Большой недостаток в том, что это не так просто понять. Вы также можете написать конечный автомат, анализируя для каждого персонажа, каким должно быть новое состояние, и это, вероятно, будет быстрее и еще больше работы.

Другие вопросы по тегам