web-dev-qa-db-ja.com

StringBufferは、2つのオブジェクトを作成せずに、どのように追加関数を実装していますか?

面接の質問でした。 StringBuffer追加関数を実装するように依頼されました。面接後にコードを見ました。しかし、1つのオブジェクトを作成するだけで操作がどのように行われるのか理解できません。

私はこのように考えています。

String s = "orange";
s.append("Apple");

ここでは、2つのオブジェクトが作成されます。

だが

StringBuilder s = new StringBuilder("Orange");
s.append("Apple");

ここでは、1つのオブジェクトのみが作成されます。

Javaはこの操作をどのように行っていますか?

23
javaMan

まず、あなたの質問に問題があります:

String s = "orange";
s.append("Apple");

ここで2つのオブジェクトが作成されます

正解です。StringBuffer/ StringBuilder内に文字列「orange」と文字列「Apple」の2つのオブジェクトが作成されます。バッファをオーバーフローさせない限り、オブジェクトは作成されません。したがって、これらのコード行は2つまたは3つのオブジェクトを作成します。

StringBuilder s = new StringBuilder("Orange");
s.append("Apple");

ここで、1つのオブジェクトのみが作成されます

どこで取得できるかわかりません。ここでは、StringBuilderオブジェクトを1つ、「オレンジ」文字列を1つ、「Apple」文字列を1つ、合計3つのオブジェクト、またはStringBuilderバッファをオーバーフローした場合は4つ作成します。 (私は配列の作成をオブジェクトの作成として数えます)。


私はあなたの質問を、StringBuilderが新しいオブジェクトを作成せずに(バッファがオーバーフローしていないときに)追加を行うにはどうすればよいかというように読みました。

StringBuilderはスレッドセーフではない実装なので、確認する必要があります。コードは面白くて読みやすいです。インラインコメントを追加しました。

内部構造として、Stringではなくchar配列があります。最初は長さ16で構築されており、容量を超えるたびに増加します。追加する文字列がchar配列内に収まる場合は、新しいオブジェクトを作成する必要はありません。

StringBuilder extends AbstractStringBuilder 、次のコードがあります。

/**
 * The value is used for character storage.
 */
char value[];

特定の時間にすべての配列が使用されるわけではないため、もう1つの重要な変数は長さです。

/**  
 * The count is the number of characters used.
 */
int count;

追加のオーバーロードはたくさんありますが、最も興味深いのは次のとおりです。

public AbstractStringBuilder append(String str) {
    if (str == null) str = "null"; //will literally append "null" in case of null
    int len = str.length(); //get the string length
    if (len == 0) return this; //if it's zero, I'm done
    int newCount = count + len; //tentative new length
    if (newCount > value.length) //would the new length fit?
        expandCapacity(newCount); //oops, no, resize my array
    str.getChars(0, len, value, count); //now it will fit, copy the chars 
    count = newCount; //update the count
    return this; //return a reference to myself to allow chaining
}

String.getChars (int srcBegin、int srcEnd、char [] dst、int dstBegin)この文字列から宛先文字配列に文字をコピーします。

したがって、appendメソッドは非常に単純で、発見する必要がある唯一の魔法はexpandCapacityです。

void expandCapacity(int minimumCapacity) {
    //get the current length add one and double it
    int newCapacity = (value.length + 1) * 2; 
    if (newCapacity < 0) { //if we had an integer overflow
        newCapacity = Integer.MAX_VALUE; //just use the max positive integer
    } else if (minimumCapacity > newCapacity) { //is it enough?
        //if doubling wasn't enough, use the actual length computed
        newCapacity = minimumCapacity;
    }
    //copy the old value in the new array
    value = Arrays.copyOf(value, newCapacity); 
}

Arrays.copyOf (char [] original、int newLength)指定された配列をコピーし、(必要に応じて)null文字で切り捨てるかパディングして、コピーが指定された長さになるようにします。

私たちの場合、長さを拡張しているので、パディング。

49
stivlo

ソース はあなたの友達、ルークです!

AbstractStringBuilder のソースは次のとおりです

9
LordDoskias

Stringは不変です。文字列を追加すると、新しい文字列のみが生成されます。

StringBuilderは変更可能です。 StringBuilderへの追加は、ArrayListへの追加のようなインプレース操作です。

4
SLaks

これはコンパイルされません。

String S= "orange";
S.append("Apple");

もしあなたがそうするなら

final String S= "orange";
final S2 = S + "Apple";

これは、コンパイル時に2つの文字列リテラルに最適化されるため、オブジェクトを作成しません。

StringBuilder s = new StringBuilder("Orange");
s.append("Apple");

これにより、2つのオブジェクトStringBuilderchar[]ラップします。使用する場合

String s2 = s.toString();

これにより、さらに2つのオブジェクトが作成されます。

もしあなたがそうするなら

String S= "orange";
S2 = S + "Apple";

これはと同じです

String S2 = new StringBuilder("orange").append("Apple").toString();

これにより、2 + 2 = 4個のオブジェクトが作成されます。

3
Peter Lawrey
String s = "orange";
s.append("Apple");

文字列でappendメソッドが使用できないため、正しくありません。

2
Raja

StringBufferは、StringBuilderと同様に、追加した文字列をコピーするcharの配列を割り当てます。文字数が配列のサイズを超えた場合にのみ新しいオブジェクトを作成します。その場合、配列を再割り当てしてコピーします。

2
Matthew Farwell

StringBuildercharsのバッファをchar[]に保持し、Stringが呼び出されたときにそれらをtoStringに変換しています。

1
John B

tl; dr:簡単に言うと、_+_文字を使用した各文字列の連結新しいStringオブジェクトにつながり、最初の文字列の内容が新しいオブジェクトにコピーされます。 StringBufferは、必要な場合にのみ拡張される内部構造を保持し、その文字がそれに追加されます。

ねえ、でも多くの人が_+_文字列連結を使用しています!

まあ、私たち/彼らはすべきではありません。

メモリ使用量に関しては、文字を保持するためにStringBufferの配列を使用しています。これはサイズ変更ですが、サイズ変更に適用されるアルゴリズムが効率的であり、Stringが1つしかない場合はめったにありません。 toString()が呼び出されると作成されるオブジェクト。これは、各_+_連結で新しいStringオブジェクトを作成するよりもはるかに優れています。

時間計算量に関しては、文字は__chars_から新しい文字列に1回だけコピーされます(O(n)時間計算量)。これは一般に、_+_演算子を使用した文字列連結よりも優れている必要があります。 、各操作で文字の新しいコピーが新しいオブジェクトになり、O(1 + 2 + .... + n) = O(n^2)操作になります。

自分で実装する必要がありますか?

演習の観点からは良いでしょうが、現代の言語は、本番コードで使用するためのネイティブのStringBuffer実装を提供します。

4つの簡単なステップで:

  1. 固定の初期サイズの文字の配列(名前を__chars_)を内部的に(プライベートに)保持するMyCustomStringBuilderクラスを作成します。この配列は文字列文字を保持します。
  2. 保持文字列の文字長がその長さを超えると、__chars_のサイズを大きくする展開メソッドを追加します。 (実際に行っているのは、ArrayListの単純なバージョンを内部的に実装することです)。
  3. stringBufferInstance.append(String s)メソッドを使用する場合は、__chars_に文字を追加し、必要に応じてサイズを大きくします。
  4. toString()メソッドの実装では、 配列を使用した文字列 を作成するだけです。

    _public String toString() {
        return new String(_chars);
    }
    _
1

他の人が説明したように、StringBufferは可変であり、char配列を使用して実装されます。 StringBufferの操作は、インプレース操作です。

詳細については、次のリンクから入手できます http://www.concentric.net/~ttwang/tech/jfastbuf.htm

char配列を使用した単純なStringBufferの実装を示しています。

0
Upul Bandara