Я не думаю, что вы должны пытаться найти более простое или короткое решение, а скорее подумайте о семантике и эффективности вашего подхода.
Вы перебираете карту, которая, вероятно, не имеет указанной итерациипорядок (например, HashMap
) и выполнение одной замены за другой, используя результат замены в качестве входных данных для следующего, потенциально пропущенные совпадения из-за ранее примененных замен или замены содержимого в заменах.
Даже если мыПредположим, что вы передаете карту, ключи и значения которой не имеют помех, этот подход очень неэффективен.Далее обратите внимание, что replaceAll
будет интерпретировать аргументы как регулярные выражения.
Если мы предполагаем, что регулярные выражения не предназначены, мы можем очистить неоднозначности между ключами, отсортировав их по длине, чтобы сначала пытались использовать более длинные ключи.Тогда решение, выполняющее одну операцию замены, может выглядеть следующим образом:
private static String replace(String text, Map<String, String> map) {
if(map.isEmpty()) return text;
String pattern = map.keySet().stream()
.sorted(Comparator.comparingInt(String::length).reversed())
.map(Pattern::quote)
.collect(Collectors.joining("|"));
Matcher m = Pattern.compile(pattern).matcher(text);
if(!m.find()) return text;
StringBuffer sb = new StringBuffer();
do m.appendReplacement(sb, Matcher.quoteReplacement(map.get(m.group())));
while(m.find());
return m.appendTail(sb).toString();
}
начиная с Java 9, вы можете использовать StringBuilder
вместо StringBuffer
здесь
Если вы протестируете его с помощью
Map<String, String> map = new HashMap<>();
map.put("f", "F");
map.put("foo", "bar");
map.put("b", "B");
System.out.println(replace("foo, bar, baz", map));
, вы получите
bar, Bar, Baz
, демонстрирующий, что замена foo
имеет приоритет над заменой f
и b
в пределах его замены bar
не заменяется.
Другое дело, если вы захотите снова заменить совпадения внутри замен.В этом случае вам понадобится либо механизм для управления заказом, либо реализация повторной замены, которая будет возвращаться только тогда, когда больше нет совпадений.Конечно, последний требует осторожности, чтобы обеспечить замены, которые в конечном итоге всегда будут приводить к результату.
Например
private static String replaceRepeatedly(String text, Map<String, String> map) {
if(map.isEmpty()) return text;
String pattern = map.keySet().stream()
.sorted(Comparator.comparingInt(String::length).reversed())
.map(Pattern::quote)
.collect(Collectors.joining("|"));
Matcher m = Pattern.compile(pattern).matcher(text);
if(!m.find()) return text;
StringBuffer sb;
do {
sb = new StringBuffer();
do m.appendReplacement(sb, Matcher.quoteReplacement(map.get(m.group())));
while(m.find());
m.appendTail(sb);
} while(m.reset(sb).find());
return sb.toString();
}
Map<String, String> map = new HashMap<>();
map.put("a", "e1");
map.put("e", "o2");
map.put("o", "x3");
System.out.println(replaceRepeatedly("foo, bar, baz", map));
fx3x3, bx321r, bx321z