簡體   English   中英

Java 8將HashMap上的Map減少為lambda

[英]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越過替換優先fb它的替換內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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM