StringBuilder 与 String 考虑替换

发布于 2024-10-17 02:05:21 字数 1056 浏览 6 评论 0原文

在连接大量字符串时,建议我使用 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";

这会导致创建相当多的字符串,而不是一个。

现在,我需要做一件类似的事情,但我不使用串联,而是使用 String 的 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 并创建大量冗长的行(如上面的行)更好?”

When doing concatenating lots of strings, I have been recommended to do it using a StringBuilder as such:

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

as opposed to

String someString = "abc";
someString = someString + "def";
someString = someString + "123";
someString = someString + "moreStuff";

which would result in the creation of quite a few Strings, as opposed to one.

Now, I need to do a similar thing, but instead of using concatenation I use the replace method of String as such:

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");

To accomplish the same thing using StringBuilder, I have to do this, just for one replace:

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

So my question is: "Is it better to use String.replace and have lots of extra Strings created, or to use StringBuilder still, and have lots of long winded lines such as the one above?"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

蒲公英的约定 2024-10-24 02:05:22

你猜怎么着?如果您使用 Java 1.5+ 运行,则连接的工作方式与字符串文字相同

  String h = "hello" + "world";

,并且

  String i = new StringBuilder().append("hello").append("world").toString();

是相同的。

所以,编译器已经为你完成了这项工作。

当然更好的是:

 String j = "hellworld"; // ;) 

至于第二个,是的,这是首选,但应该不那么难,具有“搜索和替换”的功能和一些正则表达式 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 );
    }
  }

您的代码当前看起来像这样:

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 );
    }
  }
}

Guess what? If you are running with Java 1.5+ the concatenation works the same with string literals

  String h = "hello" + "world";

and

  String i = new StringBuilder().append("hello").append("world").toString();

Are the same.

So, the compiler did the work for you already.

Of course better would be:

 String j = "hellworld"; // ;) 

As for the second, yeap, that's preferred, but should't be that hard, with the power of "search and replace" and a bit of regex foo

For instance you can define a method like the one in this sample:

  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 );
    }
  }

And your code currently looks like this:

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

All you have to do is grab text editor an trigger something like this vi search and replace:

%s/^.*("\(.*\)".\s"\(.*\)");/replace("\1","\2",builder);

That read: "take anything in parenthesis and that looks like a string literal, and put it in this other string".

And your code will look from this:

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

to this:

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

In no time.

Here's a working demo:

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 );
    }
  }
}
浅忆 2024-10-24 02:05:22

我会说使用 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"):

I would say go for using StringBuilder but simply write a wrapper that facilitates making the code more readable and thus more maintainable, while still maintaining efficiency. =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);
    }
}

OUTPUT:

new new olD dudely dowrite == pwn

Now you can just use the new version that is one easy liner

MyStringBuilder mySB = new MyStringBuilder();
mySB.append("old dudley dowrite == pwn");
mySB.replace("old", "new"):
薯片软お妹 2024-10-24 02:05:22

您可以编写一个方法来替换 StringBuilder 字符串的一部分,而不是使用这样的长行,大致如下:

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

Instead of having long lines like that, you could just write a method for replacing parts of StringBuilder strings, something along the lines of this:

public StringBuilder replace(StringBuilder someString, String replaceWhat, String replaceWith) {
   return someString.replace(someString.indexOf(replaceWhat), someString.indexOf(replaceWhat)+replaceWhat.length(), replaceWith);
}
明月松间行 2024-10-24 02:05:22

可能是 String 类内部使用

索引

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

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

May be the String Class internally uses

indexOf

method to find index of old string and replace it with new string.

And also StringBuilder is not threadsafe so it executes much faster.

蝶舞 2024-10-24 02:05:22

如果您的字符串确实很大并且您担心性能,我建议您编写一个类,该类采用模板文本和变量列表,然后逐个字符读取源字符串并使用 StringBuilder 构建结果。就 CPU 和内存使用而言,这应该是最高效的。另外,如果您从文件中读取此模板文本,我不会预先将其全部加载到内存中。当您从文件中读取它时,对其进行分块处理。

如果您只是在寻找一种构建字符串的好方法,该方法的效率不如 StringBuilder 但比一遍又一遍地附加字符串更有效,您可以使用 String.format()。它的工作方式类似于 C 语言中的 sprintf()。 MessageFormat.format() 也是一个选项,但它使用 StringBuffer。

这里还有另一个相关问题: Inserting a Java string in another string没有串联?

If your string really is large and you're worried about performance I would recommend writing a class which takes your template text and a list of variables, then reads over the source string character by character and builds the result using StringBuilder. That should be the most efficient both in terms of CPU and memory usage. Also, if you are reading this template text from a file I wouldn't load it all into memory up front. Process it in chunks as you read it from the file.

If you're just looking for a nice way to build a string that's not quite as efficient as StringBuilder but more efficient than appending strings over and over you can use String.format(). It works like sprintf() in C. MessageFormat.format() is an option too but it uses StringBuffer.

There is another related question here: Inserting a Java string in another string without concatenation?

〆一缕阳光ご 2024-10-24 02:05:22

所有家伙的代码都有一个错误。尝试 yourReplace("x","xy")。它会无限循环

All guys' codes have a bug .try yourReplace("x","xy").It will loop infinitely

冷默言语 2024-10-24 02:05:22

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;
        }
    }
}

Jam Hong is correct - the above solutions all contain the potential to loop infinitely. I guess the lesson to take away here is that micro optimisations can often cause all sorts of horrible issues and don't really save you much. Still, be that as it may - here is a solution that will not infinite loop.

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;
        }
    }
}
我喜欢麦丽素 2024-10-24 02:05:22

虽然微观优化确实可能存在问题,但它有时取决于上下文,例如,如果您的替换恰好在具有 10000 次迭代的循环内运行,您将看到与“无用”优化的显着性能差异。

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

while it's true that micro optimizations can be problematic, it sometimes depends on the context, for instance, if your replace happens to run inside of a loop with 10000 iterations, your will see a significant performance difference from the "useless" optimizations.

in most cases however, it's best to err on the side of readability

太阳哥哥 2024-10-24 02:05:21

确实,StringBuilder 往往比手动连接或修改 String 更好,因为 StringBuilder 是可变的,而 String 是不可变的,您需要为每次修改创建一个新的 String。

但需要注意的是,Java 编译器会自动将这样的示例转换为:

String result = someString + someOtherString + anotherString;

转换为类似的内容:

String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString();

也就是说,除非您要替换大量字符串,否则选择更具可读性和更可维护性的那个。微优化的悲惨悲剧。

PS

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

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

不需要

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

搜索字符串两次:-)

It is true that StringBuilder tends to be better than concatenating or modifying Strings manually, since StringBuilder is mutable, while String is immutable and you need to create a new String for each modification.

Just to note, though, the Java compiler will automatically convert an example like this:

String result = someString + someOtherString + anotherString;

into something like:

String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString();

That said, unless you're replacing a whole lot of Strings, go for whichever is more readable and more maintainable. So if you can keep it cleaner by having a sequence of 'replace' calls, go ahead and do that over the StringBuilder method. The difference will be negligible compared to the stress you save from dealing with the sad tragedy of micro-optimizations.

PS

For your code sample (which, as OscarRyz pointed out, won't work if you have more than one "$VARIABLE1" in someString, in which case you'll need to use a loop), you could cache the result of the indexOf call in:

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

With

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

No need to search the String twice :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文