【问题标题】:Use hashmap value to replace String get Error使用 hashmap 值替换 String 获取错误
【发布时间】:2018-04-21 13:01:05
【问题描述】:

我有一个String ("abc|a&b"),我想用其他Strings 替换部分String。替换存储在HashMap<String, String> 中,如下所示:

  1. "a" > "true"
  2. "b" > "false"
  3. "abc" > "true"

我的结果是"truefalsec|true&false",但我需要"true|true&false"

我的代码:

for(Map.Entry<String, String> entry: map.entrySet()) {
    if(expression.contains(entry.getKey()))
        expression = expression.replace(entry.getKey(), entry.getValue());
}
System.out.println(expression);

大家有什么建议吗?

【问题讨论】:

  • 按长度递减对密钥进行排序,然后按该顺序应用它们。

标签: java string replace hashmap


【解决方案1】:

问题在于您重新开始扫描以进行下一次替换,并且您首先扫描最短的输入。

通过在abc 之前扫描a,您将永远不会匹配abc,因为在扫描abc 之前,a 已被其他内容替换。按长度对要扫描的部分进行排序,最长在前,如commented by rgettman

通过对每个输入重新开始扫描,您可以替换替换值的内容。例如。如果您在a 之前扫描bb 会被false 替换,但随后false 中的a 会被true 替换,从而导致b 开始被ftruelse 替换.

要解决第二个问题并提高性能,您应该只扫描一次输入。

扫描一次输入(扫描多个文本之一)的最简单方法是使用像abc|a|b 这样的正则表达式,即列出以| 分隔的键,最长的键在前。

假设替换映射可以是任何东西,您应该从 Map 动态构建正则表达式,记住在映射中引用键和值,因此任何特殊字符都不会被视为正则表达式特殊字符,例如匹配任意字符.

这是使用正则表达式替换循环完成所有这些操作的方法(在 Java 8 中):

public static String replace(String input, Map<String, String> replacementValues) {
    String regex = replacementValues.keySet().stream()
                   .sorted(Comparator.comparingInt(String::length).reversed()
                                     .thenComparing(Function.identity()))
                   .map(Pattern::quote)
                   .collect(Collectors.joining("|"));
    StringBuffer buf = new StringBuffer(input.length() + 16);
    Matcher m = Pattern.compile(regex).matcher(input);
    while (m.find())
        m.appendReplacement(buf, Matcher.quoteReplacement(replacementValues.get(m.group())));
    return m.appendTail(buf).toString();
}

测试

Map<String, String> map = new HashMap<>();
map.put("a", "true");
map.put("b", "false");
map.put("abc", "true");
System.out.println(replace("abc|a&b", map));

输出

true|true&amp;false


如果扫描应该匹配单词而不是子字符串,请将正则表达式编译为:

Pattern.compile("\\b(?:" + regex + ")\\b")

【讨论】:

    【解决方案2】:
        String expression = "abc|a&b";
    
        Map<String, String> map = new LinkedHashMap<>();
    
        map.put("a", "true");
        map.put("abc", "true");
        map.put("b", "false");
    
        List<String> parts = Arrays.stream(expression.split("\\W+")).filter(map::containsKey)
                .map(map::get).collect(Collectors.toList());
    
        Queue<String> separators = new LinkedList<>(Arrays.asList(expression.split("\\w+"))
                .stream().filter(s -> !s.isEmpty())
                .collect(Collectors.toList()));
    
        StringBuilder result = new StringBuilder();
    
        for(String part : parts){
            result.append(part).append(!separators.isEmpty() ? separators.poll() : "");
        }
    
        System.out.println(result.toString());
    

    您可以使用流从表达式中获取所有键,然后对分隔符使用相同的键 -> 更改您需要的内容并将整个内容合并回字符串。

    【讨论】:

      【解决方案3】:

      问题是,您首先替换ab。然后没有abc 仍然存在并且它不会被替换。您可以使用 LinkedHashMap 并按照您希望它们被替换的顺序插入元素。这很有效,因为 LinkedHashMap 允许您按照插入它们的顺序迭代其值,而 HashMap 将以取决于键 hashCode 的顺序返回它们。

      Map<String, String> map = new LinkedHashMap<>();
      map.put("abc", "true");
      map.put("a", "true");
      map.put("b", "false");
      
      for(Map.Entry<String, String> entry: map.entrySet()) {
          expression = expression.replace(entry.getKey(), entry.getValue());
      }
      System.out.println(expression);
      

      【讨论】:

        猜你喜欢
        • 2013-10-05
        • 2014-01-02
        • 2012-04-12
        • 2014-03-30
        • 2013-05-27
        • 1970-01-01
        • 1970-01-01
        • 2020-01-08
        • 2012-09-13
        相关资源
        最近更新 更多