[英]Java 8 Map Reduce on HashMap as lambda
我有一個String
,想要替換其中的一些單詞。 我有一個HashMap
,其中鍵是要替換的占位符,以及要替換它的單詞的值。 這是我的老派代碼:
private String replace(String text, Map<String, String> map) {
for (Entry<String, String> entry : map.entrySet()) {
text = text.replaceAll(entry.getKey(), entry.getValue());
}
return text;
}
有沒有辦法將此代碼編寫為lambda表達式?
我嘗試了entrySet().stream().map(...).reduce(...).apply(...);
但無法使其發揮作用。
提前致謝。
我不認為您應該嘗試找到更簡單或更簡短的解決方案,而是考慮您的方法的語義和效率。
您正在迭代可能沒有指定迭代順序的地圖(如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
您可以使用以下每個循環:
private String replace(String text, Map<String, String> map) {
final StringBuilder sb = new StringBuilder(text);
map.forEach((k,v)->replaceAll(sb, k, v));
return sb.toString();
}
替換所有方法可以定義為:
public static void replaceAll(StringBuilder builder, String from, String to){
int index = builder.indexOf(from);
while (index != -1)
{
builder.replace(index, index + from.length(), to);
index += to.length(); // Move to the end of the replacement
index = builder.indexOf(from, index);
}
}
如果使用多線程,也可以使用StringBuffer
。
@RavindraRanwala的代碼很少有改進。
String replacement = Stream.of(text.split("\\b"))
.map(token -> map.getOrDefault(token, token))
.collect(Collectors.joining(""));
1)使用Java 8中的Map.getOrDefault
2)用“\\ b”分割以支持單詞的任何分隔符,而不僅僅是空格字符
這是一個更強大的解決方案,假設每個單詞由空格字符分隔,
String replacement = Stream.of(source.split(" ")).map(token -> map.get(token) != null ? map.get(token) : token)
.collect(Collectors.joining(" "));
出於實際目的,您的非流代碼就可以了。 作為一個有趣的練習,您可以將每個映射表示為Function<String,String>
並減少函數:
Function<String,String> combined = map.entrySet().stream()
.reduce(
Function.identity(),
(f, e) -> x -> f.apply(x).replaceAll(e.getKey(), e.getValue()),
Function::andThen
);
return combined.apply(text);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.