web-dev-qa-db-ja.com

置換を考慮したStringBuilderとString

多くの文字列を連結する場合、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";

これにより、1つではなく、かなりの数の文字列が作成されます。

次に、同様のことを行う必要がありますが、連結を使用する代わりに、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を使用して同じことを行うには、1つの置換だけでこれを行う必要があります。

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

だから私の質問は、「String.replaceを使用して多くの追加の文字列を作成した方がいいですか、それともStringBuilderをまだ使用していて、上記のような長い曲がりくねった線がたくさんあるのですか?」

32
DanielGibbs

StringBuilderはミュータブルであり、Stringは不変であり、変更ごとに新しいストリングを作成する必要があるため、StringBuilderはストリングを手動で連結または変更するよりも優れている傾向があります。

ただし、Javaコンパイラは、このような例を自動的に変換します。

String result = someString + someOtherString + anotherString;

次のようなものに:

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

とはいえ、多くの文字列を置き換える場合を除き、読みやすく保守しやすい方を使用します。したがって、よりクリーンに保つことができる場合'replace'呼び出しのシーケンスがある場合は、先に進み、StringBuilderメソッドを介してそれを実行します。 マイクロ最適化の悲しい悲劇 に対処することで節約できるストレスと比較すると、違いはごくわずかです。

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

文字列を2回検索する必要はありません:-)

44
Zach L

何だと思う? Java 1.5+で実行している場合、連結は文字列リテラルと同じように機能します

  String h = "hello" + "world";

そして

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

同じだ。

ですから、コンパイラーはすでにあなたのために仕事をしてくれました。

もちろん、より良いでしょう:

 String j = "hellworld"; // ;) 

2番目については、そうですが、「検索と置換」の機能と少しの正規表現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 );
    }
  }
}
7
OscarRyz

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"):
3
Feisty Mango

このように長い行を作成する代わりに、次のように、StringBuilder文字列の一部を置き換えるメソッドを作成できます。

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

文字列が本当に大きく、パフォーマンスが心配な場合は、テンプレートテキストと変数のリストを受け取り、ソース文字列を1文字ずつ読み取り、StringBuilderを使用して結果を構築するクラスを作成することをお勧めします。これは、CPUとメモリの使用量の両方の点で最も効率的です。また、このテンプレートテキストをファイルから読み込んでいる場合は、すべてをメモリに事前にロードしません。ファイルから読み取るときに、チャンクで処理します。

StringBuilderほど効率的ではないが、文字列を何度も追加するよりも効率的な文字列を作成するための素晴らしい方法を探しているだけの場合は、 String.format() を使用できます。 Cのsprintf()のように機能します。 MessageFormat.format() もオプションですが、StringBufferを使用します。

ここに別の関連する質問があります: Java文字列を連結せずに別の文字列に挿入しますか?

0
Sarel Botha

すべての人のコードにバグがあります.try yourReplace("x","xy")loop無限に

0
Jam Hong

Stringクラスが内部で使用する可能性があります

の指標

古い文字列のインデックスを見つけて新しい文字列に置き換えるメソッド。

また、StringBuilderはスレッドセーフではないため、実行速度が大幅に向上します。

0
Ankit

マイクロ最適化が問題になる可能性があることは事実ですが、それはコンテキストに依存することがあります。たとえば、置換がループ内で10000回の反復で実行された場合、「役に立たない」最適化とはパフォーマンスが大きく異なります。

ただし、ほとんどの場合、読みやすさの面で誤解するのが最善です。

0
Austin_Anderson

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