StringBuilderの最後の文字を削除しますか?


423

コレクションをループして、区切り文字で区切られた各データの文字列を作成する必要がある場合、常に最後に余分な区切り文字が必要になります。たとえば、

for (String serverId : serverIds) {
  sb.append(serverId);
   sb.append(",");
}

のようなものを与えます:serverId_1、serverId_2、serverId_3、

StringBuilderの最後の文字を削除したいと思います(このループの後も必要なので、変換せずに)。


11
文字列を結合することによって「文字列の連結」を意味する場合、それは文字列の数と長さに依存します。文字列は不変であるため、サイズに関係なく多くの文字列を叩く場合は、文字列ビルダーを使用する方が効率的です。文字列を連結するたびに、新しい結果の文字列(実際にはchar配列)が作成されます。文字列ビルダーは基本的に、toString()メソッドを呼び出すまで不変の文字列にならないcharのリストです。
dyslexicanaboko 2012

4
Java 8を使用している場合は、次を使用してくださいStringJoinerstackoverflow.com/a/29169233/901641
ArtOfWarfare

回答:


640

他の人がこのdeleteCharAt方法を指摘しましたが、ここに別の代替アプローチがあります:

String prefix = "";
for (String serverId : serverIds) {
  sb.append(prefix);
  prefix = ",";
  sb.append(serverId);
}

または、グアバJoinerクラスを使用してください:)

Java 8以降StringJoinerは、標準JREの一部です。


7
@Coronatus:いいえ、「ため」で不在の任意の文字ではなく、単一の文字。
Jon Skeet

31
prefix = "、"は実行されません。すべてのループサイクルがパフォーマンスに影響しますか?
24:10にHarish

21
@Harish:たぶん、小さな、小さなビット-しかし、重要である可能性は非常に低いです。
Jon Skeet

4
@Harish-オプティマイザが最初のループ反復を展開する場合、おそらくまったくない。
スティーブンC

6
Apache Commonsには、Guavaに代わる別の選択肢JoinerもありますStringUtilscommons.apache.org/proper/commons-lang/javadocs/api-2.6/org/...、java.lang.Stringで)
GoRoS

419

別の簡単な解決策は次のとおりです。

sb.setLength(sb.length() - 1);

より複雑なソリューション:

上記のソリューションは、sb.length() > 0...削除する「最後の文字」があることを前提としています。その仮定を行うことができない場合、および/または仮定が正しくない場合に発生する例外に対処できない場合は、最初にStringBuilderの長さを確認してください。例えば

// Readable version
if (sb.length() > 0) {
   sb.setLength(sb.length() - 1);
}

または

// Concise but harder-to-read version of the above.
sb.setLength(Math.max(sb.length() - 1, 0));

23
とても良い解決策です。パフォーマンスへの影響は最小限で、必要なコードは最小限です:)
Alain O'Dea

186
if(sb.length() > 0){
    sb.deleteCharAt(sb.length() - 1);
}

33
これは賛成されすぎますが、効率的ではありません。system.arraycopyを実行します。@Rohit Reddy Korrapoluの発言
alianos- 2012

13
それはのために安全ではないsb.length() == 0
マティアス

これは、サロゲートペアキャラクターをプレイしても安全ですか?
rogerdpack 2015年

最後の文字が(例のように)コンマ区切り文字であるとすると、サロゲートは違いを生じません。あなたが一般化する必要がある場合は、減算separator.length()の代わりに、1
スティーブンC

61

Java 8以降、Stringクラスには静的メソッドがありますjoin。最初の引数は文字列の各ペアの間に必要な文字列で、2番目の引数はIterable<CharSequence>(両方のインターフェイスなので、List<String>動作のようなものです。したがって、これを行うことができます:

String.join(",", serverIds);

また、Java 8ではStringJoiner、要素の完全なリストを配置する前に文字列の作成を開始するシナリオで、新しいクラスを使用できます。


nvmはあなたが気にしなければあなたのために編集し、私のコメントも削除しました
Eugene

@Eugene -私は集中して、完全な答えを書き直したString.joinのではなくStringJoiner
ArtOfWarfare

37

最後の文字の出現位置を取得するだけです。

for(String serverId : serverIds) {
 sb.append(serverId);
 sb.append(",");
}
sb.deleteCharAt(sb.lastIndexOf(","));

以来lastIndexOf逆検索を実行すると、あなたはそれが最初の試みで見つけることを知っているだろう、パフォーマンスがここでの問題ではありません。

編集

私は私の答えを上げ続けているので(人々に感謝)、それに関して価値があります:

のJava 8以降、それだけで使用することがより読みやすく、明示的になりStringJoinerを。単純なセパレータの1つのメソッドと、プレフィックスとサフィックスのオーバーロードがあります。

ここからの例:

単純なセパレータを使用した例:

    StringJoiner mystring = new StringJoiner("-");    

    // Joining multiple strings by using add() method  
    mystring.add("Logan");  
    mystring.add("Magneto");  
    mystring.add("Rogue");  
    mystring.add("Storm");  

    System.out.println(mystring);

出力:

ローガンマグネトローグストーム

接尾辞と接頭辞の例:

    StringJoiner mystring = new StringJoiner(",", "(", ")");    

    // Joining multiple strings by using add() method  
    mystring.add("Negan");  
    mystring.add("Rick");  
    mystring.add("Maggie");  
    mystring.add("Daryl");  

    System.out.println(mystring);

出力

(ネガン、リック、マギー、ダリル)


最後の文字は,の最後のステートメントだったので、それは必ずですfor looplastInfexOfより読みやすくするために、それは0・インデックス付きであるかどうか、あなたが覚えてしたくない場合は、それがない非常に簡単にすることです。さらに、stringbuilderの長さをいじる必要はありません。それは便宜のためです。
Reuel Ribeiro 2017

34

この場合、

sb.setLength(sb.length() - 1);

最後の値を割り当てるだけなので'\0'、最後の文字を削除すると、System.arraycopy


1
setLength呼び出しは、最後の値には何も割り当てされていません。Java文字列バッファはnullまたはゼロで終了していません。実際にsetLengthは、単にlengthフィールドを更新しています。
スティーブンC

@Rohit Reddy Korrapolu:しかし、arraycopyコピーは0要素なので、最適化することができると思います。
maaartinus 2013年

2
newLength引数が現在の長さ以上の場合、長さがnewLength引数になるように、十分なnull文字( '\ u0000')が追加されます。そうではありません。
fglez 14年


11

別の選択肢

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}
sb.deleteCharAt(0);

2
サイズの計算が必要なため、最後の文字を削除するよりも優れているはずです。最初の文字を削除するとデータが移動しない限り...
slott

8

または、

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;

「。」を追加できるので使用可能。最後に。
artfullyContrived

6
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true

5

さらに別の選択肢:

public String join(Collection<String> collection, String seperator) {
    if (collection.isEmpty()) return "";

    Iterator<String> iter = collection.iterator();
    StringBuilder sb = new StringBuilder(iter.next());
    while (iter.hasNext()) {
        sb.append(seperator);
        sb.append(iter.next());
    }

    return sb.toString();
}

3

TextUtils.isEmptyの再初期化(パフォーマンスへの影響)を回避 prefixするには:

            String prefix = "";
            for (String item : list) {
                sb.append(prefix);
                if (TextUtils.isEmpty(prefix))
                    prefix = ",";
                sb.append(item);
            }

TestUtilsはどのようなパッケージに属していますか?
Markus

@Markus android.text.TextUtils
NickUnuchek

1

生成されたテキストから最後の文字を削除する代わりに、「Joiner」クラスを使用してみてください。

                List<String> textList = new ArrayList<>();
                textList.add("text1");
                textList.add("text2");
                textList.add("text3");

                Joiner joiner = Joiner.on(",").useForNull("null");
                String output = joiner.join(textList);

               //output : "text1,text2,text3"

1

私は以下のようなことをしています:

    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < value.length; i++) {
        stringBuilder.append(values[i]);
        if (value.length-1) {
            stringBuilder.append(", ");
        }
    }

0

ここに別の解決策があります:

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}

String resultingString = "";
if ( sb.length() > 1 ) {
    resultingString = sb.substring(1);
}

1
ああなるほど。StringBuilderでは、文字列ではなく部分文字列を呼び出しています。
スティーブンC

しかし、いずれにせよ、これは2010年からザキのソリューションでほんの少しの変形である
スティーブン・C

0

私は個人的に末尾にバックスペース文字(またはより長い「区切り文字」の場合はそれ以上)を追加するのが好きです:

for(String serverId : serverIds) {
    sb.append(serverId);
    sb.append(",");
}

sb.append('\b');

問題があることに注意してください。

  • \b表示方法は環境によって異なりますが、
  • 内容は、「visibile」の文字の長さが異なる場合がありますlength()String

\b問題なく見え、コンソールへのロギングなど、長さが問題でない場合は、これで十分です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.