我认为你不应该试图寻找更简单或更短的解决方案,而是要考虑你的方法的语义和效率。
你正在迭代一个可能没有指定迭代顺序的映射(如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
时优先级高于将其替换为bar
中的b
不被替换。
如果您需要再次在替换结果内部进行替换,则需要控制顺序或实现一个重复替换,仅当没有匹配项时才返回。当然,后者需要注意提供始终会最终收敛到结果的替换。
例如:
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