Java StringBuilder与考虑替换的字符串

Java StringBuilder与考虑替换的字符串,java,string,replace,stringbuilder,Java,String,Replace,Stringbuilder,在连接大量字符串时,建议我使用StringBuilder这样做: StringBuilder someString = new StringBuilder("abc"); someString.append("def"); someString.append("123"); someString.append("moreStuff"); 相对于 String someString = "abc"; someString = someString + "def"; someString = so

在连接大量字符串时,建议我使用
StringBuilder
这样做:

StringBuilder someString = new StringBuilder("abc");
someString.append("def");
someString.append("123");
someString.append("moreStuff");
相对于

String someString = "abc";
someString = someString + "def";
someString = someString + "123";
someString = someString + "moreStuff";
这将导致创建相当多的字符串,而不是一个

现在,我需要做一件类似的事情,但不是使用串联,而是使用字符串的
replace
方法:

String someString = SOME_LARGE_STRING_CONSTANT;
someString = someString.replace("$VARIABLE1", "abc");
someString = someString.replace("$VARIABLE2", "def");
someString = someString.replace("$VARIABLE3", "123");
someString = someString.replace("$VARIABLE4", "moreStuff");
为了使用StringBuilder完成同样的任务,我必须这样做,只需更换一次:

someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");

所以我的问题是:“使用String.replace并创建大量额外的字符串更好吗?还是仍然使用StringBuilder,并创建大量像上面那样的冗长行更好?”

可能是String类内部使用的字符串

索引

方法查找旧字符串的索引并将其替换为新字符串


而且StringBuilder不是线程安全的,因此执行速度更快。

StringBuilder确实比手动连接或修改字符串要好,因为StringBuilder是可变的,而字符串是不可变的,每次修改都需要创建一个新字符串

不过,请注意,Java编译器会自动转换如下示例:

String result = someString + someOtherString + anotherString;
someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
变成类似于:

String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString();
也就是说,除非您要替换大量字符串,选择可读性和可维护性更好的字符串。因此,如果您可以通过一系列的“替换”调用来保持它的简洁性,那么就通过StringBuilder方法来做吧。与处理压力相比,这种差异可以忽略不计

PS

对于您的代码示例(正如OscarRyz指出的,如果在
someString
中有多个
“$VARIABLE1”
,则该示例不起作用,在这种情况下,您需要使用循环),您可以缓存
indexOf
调用的结果:

someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");


无需搜索字符串两次:-)

您可以编写一个方法来替换StringBuilder字符串的某些部分,而不是像这样的长行:

public StringBuilder replace(StringBuilder someString, String replaceWhat, String replaceWith) {
   return someString.replace(someString.indexOf(replaceWhat), someString.indexOf(replaceWhat)+replaceWhat.length(), replaceWith);
}

如果您的字符串确实很大,并且您担心性能,我建议您编写一个类,该类接受模板文本和变量列表,然后逐个字符读取源字符串,并使用StringBuilder生成结果。在CPU和内存使用方面,这应该是最有效的。此外,如果您正在从文件中读取此模板文本,我不会预先将其全部加载到内存中。从文件中读取时,将其分块处理

如果您只是在寻找一种很好的方法来构建一个字符串,它的效率不如StringBuilder,但比反复追加字符串更有效,那么您可以使用它。它的工作原理与C中的sprintf()类似。也是一个选项,但它使用StringBuffer


这里还有另一个相关的问题:

猜怎么着?如果您使用Java 1.5+运行,那么连接对字符串文本的作用是相同的

  String h = "hello" + "world";

都是一样的

那么,编译器已经为您完成了这项工作

当然,最好是:

 String j = "hellworld"; // ;) 
至于第二个,是的,这是首选,但不应该那么难,具有“搜索和替换”的功能和一点regex-foo

例如,您可以定义一个类似于此示例中的方法:

  public static void replace( String target, String replacement, 
                              StringBuilder builder ) { 
    int indexOfTarget = -1;
    while( ( indexOfTarget = builder.indexOf( target ) ) >= 0 ) { 
      builder.replace( indexOfTarget, indexOfTarget + target.length() , replacement );
    }
  }
您的代码当前如下所示:

String result = someString + someOtherString + anotherString;
someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
你所要做的就是抓取文本编辑器一个触发器,比如vi搜索和替换:

%s/^.*("\(.*\)".\s"\(.*\)");/replace("\1","\2",builder);
上面写着:“把括号中任何看起来像字符串文字的东西放在另一个字符串中。”

您的代码将如下所示:

someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
为此:

replace( "VARIABLE1", "abc", builder );
replace( "VARIABLE2", "xyz", builder );
很快

这是一个有效的演示:

class DoReplace { 
  public static void main( String ... args ) {
    StringBuilder builder = new StringBuilder(
       "LONG CONSTANT WITH VARIABLE1 and  VARIABLE2 and VARIABLE1 and VARIABLE2");
    replace( "VARIABLE1", "abc", builder );
    replace( "VARIABLE2", "xyz", builder );
    System.out.println( builder.toString() );
  }
  public static void replace( String target, String replacement, 
                              StringBuilder builder ) { 
    int indexOfTarget = -1;
    while( ( indexOfTarget = builder.indexOf( target ) ) > 0 ) { 
      builder.replace( indexOfTarget, indexOfTarget + target.length() , 
                       replacement );
    }
  }
}

我想说的是使用StringBuilder,但只需编写一个包装器,使代码更具可读性,从而更易于维护,同时保持效率=D

import java.lang.StringBuilder;
public class MyStringBuilder
{
    StringBuilder sb;

    public MyStringBuilder() 
    {
       sb = new StringBuilder();
    }

    public void replace(String oldStr, String newStr)
    {
            int start = -1;
            while ((start = sb.indexOf(oldStr)) > -1)
            {
                    int end = start + oldStr.length(); 
                    sb.replace(start, end, newStr);
            }
    }

    public void append(String str)
    {
       sb.append(str);
    }

    public String toString()
    {
          return sb.toString();
    }

    //.... other exposed methods

    public static void main(String[] args)
    {
          MyStringBuilder sb = new MyStringBuilder();
          sb.append("old old olD dudely dowrite == pwn");
          sb.replace("old", "new");
          System.out.println(sb);
    }
}
输出:

new new olD dudely dowrite == pwn
现在,您可以只使用一个简单的衬里的新版本

MyStringBuilder mySB = new MyStringBuilder();
mySB.append("old dudley dowrite == pwn");
mySB.replace("old", "new"):

所有人的代码都有一个bug。试试你的替换(“x”,“xy”)。它将无限循环。Jam Hong是正确的-上述解决方案都包含无限循环的可能性。我想这里要吸取的教训是,微观优化往往会导致各种可怕的问题,并不会真正为你节省太多。尽管如此,这是一个不会无限循环的解决方案

private static void replaceAll(StringBuilder builder, String replaceWhat, String replaceWith){
    int occuranceIndex = builder.indexOf(replaceWhat);
    int lastReplace = -1;
    while(occuranceIndex >= 0){
        if(occuranceIndex >= lastReplace){
            builder.replace(occuranceIndex, occuranceIndex+replaceWhat.length(), replaceWith);
            lastReplace = occuranceIndex + replaceWith.length();
            occuranceIndex = builder.indexOf(replaceWhat);
        }else{
            break;
        }
    }
}

虽然微优化确实会有问题,但有时这取决于上下文,例如,如果您的替换恰好在一个10000次迭代的循环中运行,那么您将看到与“无用”优化的显著性能差异


但是,在大多数情况下,最好是在可读性方面出错

如果输入中有两个$VARIABLE1,则此操作失败(它只替换了第一个。你必须把它放在一个while循环中。@OscarRyz D'oh!是的。我甚至没有注意到:-p。我编写代码示例的主要原因只是向提问者指出,他们不应该调用indexOf两次。顺便说一句,这是一个很好的观点。我在一个示例中尝试了同样的方法,直到那时才意识到。这很容易伪造。)t、 看我的ans@OscarRyz@ZachL:Example:
builder=newstringbuilder(120);builder.append(a()).append(b()).append(c())
Hi,如果我们知道最终的大小,比如说输出字符串将是120,那么上面的代码会比编译过程中自动生成的代码更好吗。注意,方法a、b、c不会返回静态硬编码字符串,所以编译器无法知道初始化生成器时使用的值。如果生成性能问题,请更改它。如果没有更重要的更改要执行,请更改它。如果输入非常大并且经常使用,请更改它。顺便说一句,secont方法不起作用,因为它只会替换它一次,您必须将它放在一个while循环中。看看我的答案。我也在考虑相同的问题,但这需要一个while循环来替换它们,不仅如此