【问题标题】:How to rewrite this block of code using a StringBuilder in Java?如何在 Java 中使用 StringBuilder 重写这段代码?
【发布时间】:2023-04-10 18:32:01
【问题描述】:

给定一个词,我必须用一些特定的字母替换一些特定的字母,例如 1 代表 a,5 代表 b 等。我为此使用正则表达式。我知道 StringBuilder 是处理这个问题的最好方法,因为我正在做很多字符串操作。这是我正在做的事情:

String word = "foobooandfoo";
String converted = "";
converted = word.replaceAll("[ao]", "1");
converted = converted.replaceAll("[df]", "2");
converted = converted.replaceAll("[n]", "3");

我的问题是如何使用 StringBuilder 重写这个程序。我尝试了一切,但我无法成功。或者使用 String 就可以了?

【问题讨论】:

    标签: java string stringbuilder


    【解决方案1】:

    我认为这是一个清晰和性能完美结合的案例。我会使用查找表来进行“翻译”。

      public static void translate(StringBuilder str, char[] table)
      {
        for (int idx = 0; idx < str.length(); ++idx) {
          char ch = str.charAt(idx);
          if (ch < table.length) {
            ch = table[ch];
            str.setCharAt(idx, ch);
          }
        }
      }
    

    如果str 输入的字母较大,或者您的映射很稀疏,您可以使用真实的映射,如下所示:

      public static void translate(StringBuilder str, Map<Character, Character> table)
      {
        for (int idx = 0; idx < str.length(); ++idx) {
          char ch = str.charAt(idx);
          Character conversion = table.get(ch);
          if (conversion != null) 
            str.setCharAt(idx, conversion);
        }
      }
    

    虽然这些实现就地工作,但您可以创建一个新的 StringBuilder 实例(或附加到传入的实例)。

    【讨论】:

    • 清晰是旁观者,我的朋友。我发现问题中的代码更容易理解。
    • 没错,这是见仁见智的问题。使用三个规则,这不是问题,但我很难在嵌入大量样板的正则表达式字符类的长列表中发现错误。
    • 这是迄今为止最好的解决方案。任何可以在不使用映射(switch 语句)的情况下编写代码的人都不应该称自己为软件工程师。您的代码中不应包含数据(甚至“1”、“1”或“a”)
    【解决方案2】:

    实际上,我会说代码在大多数应用程序中都相当不错,尽管它在理论上不如其他方法。如果你不想使用Matcher,可以这样尝试:

    StringBuilder result = new StringBuilder(word.length());
    
    for (char c : word.toCharArray()) {
        switch (c) {
            case 'a': case 'o': result.append('1'); break;
            case 'd': case 'f': result.append('2'); break;
            case 'n': result.append('3'); break;
            default: result.append(c); break;
        }
    }
    

    【讨论】:

    • 不幸的是 for (char c : word) 似乎不起作用(至少不适用于我的 javac)。哦,对于 C#...
    • (除此之外,我们的解决方案基本相同:)
    • Err, for (char c : word.toCharArray()) 在 Java 5 中工作得非常好。这已经存在多年了......
    • (如果我们关注性能,我希望显式迭代比将字符串复制到新的 char 数组更快。)
    • 感谢 sblundy 更正我的代码,感谢 StackOverflow 成为一个 wiki!
    【解决方案3】:

    我不知道 StringBuilder 是否适合您。我会考虑查看Matcher,它是 java regex 包的一部分,并且可能比您上面的示例更快 如果您真的需要性能

    【讨论】:

      【解决方案4】:

      我不相信你可以。所有的正则表达式替换 API 都使用 String 而不是 StringBuilder。

      如果您基本上是将每个字符转换为不同的字符,则可以执行以下操作:

      public String convert(String text)
      {
          char[] chars = new char[text.length()];
          for (int i=0; i < text.length(); i++)
          {
              char c = text.charAt(i);
              char converted;
              switch (c)
              {
                  case 'a': converted = '1'; break;
                  case 'o': converted = '1'; break;
                  case 'd': converted = '2'; break;
                  case 'f': converted = '2'; break;
                  case 'n': converted = '3'; break;
                  default : converted = c; break;
              }
              chars[i] = converted;
          }
          return new String(chars);
      }
      

      但是,如果您执行任何复杂的正则表达式,那显然不会有太大帮助。

      【讨论】:

      • 这似乎是一个很好的解决方案,可能开销最小。就我个人而言,我会汇总这些 case 语句,摆脱转换后的变量并覆盖 c,然后您也可以摆脱默认 case。
      【解决方案5】:

      StringBuilder 和 StringBuffer 在某些程序中可能会有很大的性能差异。见:http://www.thectoblog.com/2011/01/stringbuilder-vs-stringbuffer-vs.html 这将是一个强烈的理由想要坚持下去。

      原帖要求将多字符替换为单字符。这会影响调整大小,进而影响性能。

      也就是说,最简单的方法是使用字符串。但是如果性能是一个问题,要注意是否这样做以最大程度地减少 gc 和其他影响。

      我喜欢 P Arrayah 的方法,但对于更通用的答案,它应该使用 LinkedHashMap 或保留顺序的东西,以防替换具有依赖性。

      Map replaceRules = new HashMap();

      Map replaceRules = new LinkedHashMap();

      【讨论】:

        【解决方案6】:

        我查看了Matcher.replaceAll(),发现它返回了String。因此,我认为你所拥有的将会非常快。正则表达式易于阅读且快速。

        记住优化的第一条规则:不要这样做!

        【讨论】:

          【解决方案7】:

          我知道 StringBuilder 是解决此问题的最佳方法,因为我正在执行大量字符串操作。

          谁对你这么说的?最好的方法是那些更容易阅读的方法,使用 StringBuilder 的方法。 StringBuilder 在某些情况下,但在许多情况下并不能提供明显的加速。

          如果值总是被替换,你不应该初始化“converted”。

          您可以删除一些样板来改进您的代码:

          String word = "foobooandfoo";
          String converted = word.replaceAll("[ao]", "1")
                                 .replaceAll("[df]", "2")
                                 .replaceAll("[n]", "3");
          

          如果你想使用 StringBuilder 你可以使用这个方法

          java.util.regex.Pattern#matcher(java.lang.CharSequence)

          接受 CharSequence(由 StringBuilder 实现)。 见http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#matcher(java.lang.CharSequence)

          【讨论】:

            【解决方案8】:

            StringBuilder 与正则表达式是错误的二分法。 String#replaceAll() 是错误工具的原因是,每次调用它时,您都在编译正则表达式并处理整个字符串。您可以通过将所有正则表达式组合为一个并使用 Matcher 中的低级方法而不是 replaceAll() 来避免所有多余的工作,如下所示:

            String text = "foobooandfoo";
            Pattern p = Pattern.compile("([ao])|([df])|n");
            Matcher m = p.matcher(text);
            StringBuffer sb = new StringBuffer();
            while (m.find())
            {
              m.appendReplacement(sb, "");
              sb.append(m.start(1) != -1 ? '1' :
                        m.start(2) != -1 ? '2' :
                                           '3');
            }
            m.appendTail(sb);
            System.out.println(sb.toString());
            

            当然,这还是大材小用;对于这样简单的工作,我推荐 Erickson 的方法。

            【讨论】:

              【解决方案9】:

              我不建议为此使用任何正则表达式,当您进行简单操作时,这些实际上都非常缓慢。相反,我建议你从这样的事情开始

              // usage:
              Map<String, String> replaceRules = new HashMap<String, String>();
              replaceRules.put("ao", "1");
              replaceRules.put("df", "2");
              replaceRules.put("n", "3");
              String s = replacePartsOf("foobooandfoo", replaceRules);
              
              // actual method
              public String replacePartsOf(String thisString, Map<String, String> withThese) {
                  for(Entry<String, String> rule : withThese.entrySet()) {
                      thisString = thisString.replaceAll(rule.getKey(), rule.getValue());
                  }
              
                  return thisString;
              }
              

              在你完成这个工作之后,重构它以使用字符数组。虽然我认为您想要做的事情可以使用 StringBuilder 完成,但很可能不值得付出努力。

              【讨论】:

              • String.replaceAll 实际上使用正则表达式。所以在 for 循环中对它的调用并不等同于原始海报提供的代码。请参阅 Javadoc。
              • 我只是想让他遵循上面sn-p的逻辑,而不是完全使用它。我确实说过他应该“重构它[代码] 以改用字符数组”。
              猜你喜欢
              • 1970-01-01
              • 2021-09-25
              • 1970-01-01
              • 1970-01-01
              • 2021-07-15
              • 1970-01-01
              • 2014-09-14
              • 2017-07-07
              • 1970-01-01
              相关资源
              最近更新 更多