web-dev-qa-db-ja.com

StringBuilder-リセットまたは新規作成

StringBuilderが大きなフラットファイル(数百MB)のパターンに一致する行を保存し続けるという条件があります。ただし、条件に達した後、StringBuilder可変の内容をテキストファイルに書き込みます。

今、私はオブジェクトをリセットすることで同じ変数を使用すべきかどうか疑問に思う->

stringBuilder.delete(0,stringBuilder.length())

OR

stringBuilder=new StringBuilder();

パフォーマンスとOOMの両方の問題に関して、どちらが良いと思いますか提案してください。

22
Chandru

StringBuilder#delete(start, end)はまだ高価な電話だと思います。

stringBuilder.setLength(0);

それをリセットします。


UPDATE:source code of StringBuilder [setLength(int)は古いバッファをそのままにして、上記の呼び出しの後にattempts to reduce storage used for the character sequenceを呼び出したほうがよい:StringBuilder#trimToSize()

したがって、このようなものはより効率的です:

stringBuilder.setLength(0); // set length of buffer to 0
stringBuilder.trimToSize(); // trim the underlying buffer
32
anubhava

私見、私は新しいを使用することをお勧めします:

stringBuilder = new StringBuilder();

StringBuilderのメモリリークについて聞いたことがありませんが、本当に限界を押し広げたとき、あなたは決して知りません。毎回新しいインスタンスを使用することで、それに対する賭けをヘッジします。

最悪の場合、効率が落ちてGCが動作する可能性がありますが、OOMの可能性は除外されます。

そのため、また明確にするために、私は個人的に新しいアプローチを採用します。

6
vikingsteve

基本的な違いの1つは、sb.deleteが参照を保持するのに対して、コンストラクターが参照を失うことです。

SBがメソッド引数であり、呼び出し元にコンテンツを戻すために使用されることになっている場合、sb.deleteを使用する必要があります。呼び出し元は元の参照を保持します。

5
user3241961

さて、両者には大きな違いがあります。 1つ目は、文字を削除する前の容量を保持します(つまり、stringBuilder.capacity())。2つ目は、デフォルトの容量16で新しいStringBuilderを作成します。もちろん、stringBuilder.capacity()コンストラクターへの引数として。ただし、ここでの違いを理解することが重要です。

いずれにせよ、これら2つのバリアントのパフォーマンスに大きな違いが生じることは非常に疑わしいので、読みやすく管理しやすい方を選択してください。 のみアプローチを変更した場合、これが何らかのボトルネックの原因であると最終的に判断した場合。

3
arshajii

理想的には、new StringBuilder()を使用する必要があります。StringBuilderクラスで grepcode から少し掘り下げれば、次のことがわかります。

新しいオブジェクトの作成:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

new StringBuilder()は、初期容量char配列を持つ新しいオブジェクトを作成します。ここのオーバーヘッド:古いオブジェクトをクリアするためにGCが呼び出されます。

deleteの使用:

public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }

LengthとTrimToSizeの使用:

public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }

配列クラスからcopyOfを呼び出します

public static char [] copyOf(char [] original、int newLength){char [] copy = new char [newLength]; System.arraycopy(original、0、copy、0、Math.min(original.length、newLength));コピーを返す; }

また、ネイティブメソッドであるSystem.arrayCopyも呼び出します。 copyOfに表示される場合、長さ0の新しいcharArrayを再度作成し、にデータを再度追加しようとすると、expandが呼び出されます現在の長さは0になるためです。だから、新しいStringBuilder()を呼び出す方が良いと思います

上記のコードは grepcode で確認できます

PS:@ user3241961は、このオブジェクトの参照を使用している場合に書き込みを行うため、newでは再度設定する必要があります

1
Akhil Dad

私は使うだろう:

_ stringBuilder = new StringBuilder();
_

大量のデータを入力すると、stringBuilder.setLength(0);を呼び出してもバッキング配列の割り当てが解除されないため、メモリ使用量が不必要に高くなることがあります。

また、読みやすく、理解しやすいだけです。

1
Bohemian

常に新しいオブジェクトを割り当てるよりも、作成したオブジェクトを再利用する方が安価です。また、オブジェクトのみを処理するため、ガベージコレクターの余分な作業を回避できます。

より高速な方法は次のとおりです。

stringBuilder.setLength(0);
1
jmlotero

緊密なループにあり、ファイルにデータを書き込んだ後、そのループに戻る場合は、StringBuilderを確実に再利用する必要があります。しない理由はなく、GCをかき回すよりもましです。これをCまたはC++で記述している場合、バッファを再利用します。

また、delete(...)メソッドがSystem.arraycopyを呼び出すことは事実ですが、コピーされるバイト数は0であるため重要ではありません。

ああ-他の誰かが、バッファを再利用する最も速い方法であるsetLength(...)メソッドがあると言った。

0
jpayne