Мне нужно заменить много разных подстрок в строке наиболее эффективным способом. есть ли другой способ, кроме метода грубой силы для замены каждого поля с помощью string.replace?
97
Если строка, с которой вы работаете, очень длинная или вы работаете со многими строками, может быть целесообразно использовать java.util.regex.Matcher (для этого требуется время предварительной компиляции, поэтому он не будет эффективным если ваш ввод очень мал или ваш шаблон поиска часто меняется).
Ниже приведен полный пример, основанный на списке токенов, взятых с карты. (Использует StringUtils из Apache Commons Lang).
Map<String,String> tokens = new HashMap<String,String>();
tokens.put("cat", "Garfield");
tokens.put("beverage", "coffee");
String template = "%cat% really needs some %beverage%.";
// Create pattern of the format "%(cat|beverage)%"
String patternString = "%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(template);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
}
matcher.appendTail(sb);
System.out.println(sb.toString());
Как только регулярное выражение скомпилировано, сканирование входной строки обычно происходит очень быстро (хотя, если ваше регулярное выражение является сложным или включает в себя отслеживание с возвратом, вам все равно потребуется выполнить тест, чтобы подтвердить это!)
"%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Алгоритм
Одним из наиболее эффективных способов замены совпадающих строк (без регулярных выражений) является использование алгоритма Aho-Corasick с эффективным Trie (произносится как «попытка»), алгоритмом быстрого хеширования и эффективной реализацией коллекций .
Простой код
Простое решение использует Apache
StringUtils.replaceEach
следующим образом:Это тормозит на больших текстах.
Быстрый код
Реализация Bor алгоритма Aho-Corasick представляет немного большую сложность, которая становится деталью реализации за счет использования фасада с той же сигнатурой метода:
Контрольные точки
Для тестов буфер был создан с использованием randomNumeric следующим образом:
Где
MATCHES_DIVISOR
диктует количество вводимых переменных:Сам код теста ( JMH казался излишним):
1 000 000: 1 000
Простой микротест с 1 000 000 символов и 1 000 случайно расположенными строками для замены.
Нет конкурса.
10 000: 1000
Использование 10000 символов и 1000 совпадающих строк для замены:
Разделение закрывается.
1000: 10
Использование 1000 символов и 10 совпадающих строк для замены:
Для коротких струн накладные расходы на настройку Aho-Corasick затмевают грубую силу
StringUtils.replaceEach
.Возможен гибридный подход, основанный на длине текста, чтобы получить лучшее от обеих реализаций.
Реализации
Рассмотрите возможность сравнения других реализаций для текста размером более 1 МБ, в том числе:
Статьи
Документы и информация по алгоритму:
источник
Это сработало для меня:
Пример:
Продукт: яблоко-банан-фрукт
источник
Если вы собираетесь менять String много раз, то обычно более эффективно использовать StringBuilder (но измерьте свою производительность, чтобы узнать) :
Каждый раз, когда вы выполняете замену String, создается новый объект String, потому что строки неизменяемы. StringBuilder является изменяемым, то есть его можно изменять сколько угодно.
источник
StringBuilder
выполнит замену более эффективно, так как его буфер массива символов может быть указан до необходимой длины.StringBuilder
предназначен не только для добавления!Конечно, вопрос в том, не заходит ли это оптимизация слишком далеко? JVM очень хорошо справляется с созданием нескольких объектов и последующей сборкой мусора, и, как и все вопросы оптимизации, мой первый вопрос заключается в том, измерили ли вы это и определили ли вы, что это проблема.
источник
Как насчет использования метода replaceAll () ?
источник
str.replaceAll(search1, replace1).replaceAll(search2, replace2).replaceAll(search3, replace3).replaceAll(search4, replace4)
Rythm, механизм шаблонов Java, теперь выпущен с новой функцией, называемой режимом интерполяции строк, которая позволяет вам делать что-то вроде:
В приведенном выше случае показано, что вы можете передавать аргумент в шаблон по позиции. Rythm также позволяет передавать аргументы по имени:
Примечание. Rythm ОЧЕНЬ БЫСТРЫЙ, примерно в 2–3 раза быстрее, чем String.format и скорость, поскольку он компилирует шаблон в байт-код Java, производительность во время выполнения очень близка к объединению с StringBuilder.
Ссылки:
источник
"%cat% really needs some %beverage%.";
не является ли этот%
разделенный токен заранее определенным форматом? Ваше первое замечание еще более забавно, JDK предоставляет множество «старых возможностей», некоторые из них начинаются с 90-х годов, зачем людям их использовать? Ваши комментарии и голосование против не имеют никакого реального смыслаПриведенное ниже основано на ответе Тодда Оуэна . У этого решения есть проблема, заключающаяся в том, что если замены содержат символы, которые имеют особое значение в регулярных выражениях, вы можете получить неожиданные результаты. Я также хотел иметь возможность выполнять поиск без учета регистра. Вот что я придумал:
Вот мои примеры модульных тестов:
источник
источник
Проверь это:
Например:
источник
Описание: Одноклассная реализация ответа Дэйва для автоматического выбора наиболее эффективного из двух алгоритмов.
Это полная реализация одного класса, основанная на приведенном выше отличном ответе Дэйва Джарвиса. . Класс автоматически выбирает между двумя различными предоставленными алгоритмами для максимальной эффективности. (Этот ответ предназначен для людей, которые просто хотят быстро скопировать и вставить.)
ReplaceStrings класс:
Необходимые зависимости Maven:
(При необходимости добавьте их в свой файл pom.)
источник