面接の質問でした。 StringBuffer
追加関数を実装するように依頼されました。面接後にコードを見ました。しかし、1つのオブジェクトを作成するだけで操作がどのように行われるのか理解できません。
私はこのように考えています。
String s = "orange";
s.append("Apple");
ここでは、2つのオブジェクトが作成されます。
だが
StringBuilder s = new StringBuilder("Orange");
s.append("Apple");
ここでは、1つのオブジェクトのみが作成されます。
Javaはこの操作をどのように行っていますか?
まず、あなたの質問に問題があります:
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文字で切り捨てるかパディングして、コピーが指定された長さになるようにします。
私たちの場合、長さを拡張しているので、パディング。
ソース はあなたの友達、ルークです!
AbstractStringBuilder のソースは次のとおりです
String
は不変です。文字列を追加すると、新しい文字列のみが生成されます。
StringBuilder
は変更可能です。 StringBuilder
への追加は、ArrayListへの追加のようなインプレース操作です。
これはコンパイルされません。
String S= "orange";
S.append("Apple");
もしあなたがそうするなら
final String S= "orange";
final S2 = S + "Apple";
これは、コンパイル時に2つの文字列リテラルに最適化されるため、オブジェクトを作成しません。
StringBuilder s = new StringBuilder("Orange");
s.append("Apple");
これにより、2つのオブジェクトStringBuilder
とchar[]
ラップします。使用する場合
String s2 = s.toString();
これにより、さらに2つのオブジェクトが作成されます。
もしあなたがそうするなら
String S= "orange";
S2 = S + "Apple";
これはと同じです
String S2 = new StringBuilder("orange").append("Apple").toString();
これにより、2 + 2 = 4個のオブジェクトが作成されます。
String s = "orange";
s.append("Apple");
文字列でappendメソッドが使用できないため、正しくありません。
StringBufferは、StringBuilderと同様に、追加した文字列をコピーするcharの配列を割り当てます。文字数が配列のサイズを超えた場合にのみ新しいオブジェクトを作成します。その場合、配列を再割り当てしてコピーします。
StringBuilder
はchar
sのバッファをchar[]
に保持し、String
が呼び出されたときにそれらをtoString
に変換しています。
tl; dr:簡単に言うと、_+
_文字を使用した各文字列の連結式新しいString
オブジェクトにつながり、最初の文字列の内容が新しいオブジェクトにコピーされます。 StringBuffer
は、必要な場合にのみ拡張される内部構造を保持し、その文字がそれに追加されます。
ねえ、でも多くの人が_+
_文字列連結を使用しています!
まあ、私たち/彼らはすべきではありません。
メモリ使用量に関しては、文字を保持するためにStringBuffer
の配列を使用しています。これはサイズ変更ですが、サイズ変更に適用されるアルゴリズムが効率的であり、String
が1つしかない場合はめったにありません。 toString()
が呼び出されると作成されるオブジェクト。これは、各_+
_連結で新しいString
オブジェクトを作成するよりもはるかに優れています。
時間計算量に関しては、文字は__chars
_から新しい文字列に1回だけコピーされます(O(n)
時間計算量)。これは一般に、_+
_演算子を使用した文字列連結よりも優れている必要があります。 、各操作で文字の新しいコピーが新しいオブジェクトになり、O(1 + 2 + .... + n) = O(n^2)
操作になります。
自分で実装する必要がありますか?
演習の観点からは良いでしょうが、現代の言語は、本番コードで使用するためのネイティブのStringBuffer
実装を提供します。
4つの簡単なステップで:
_chars
_)を内部的に(プライベートに)保持するMyCustomStringBuilder
クラスを作成します。この配列は文字列文字を保持します。_chars
_のサイズを大きくする展開メソッドを追加します。 (実際に行っているのは、ArrayList
の単純なバージョンを内部的に実装することです)。stringBufferInstance.append(String s)
メソッドを使用する場合は、__chars
_に文字を追加し、必要に応じてサイズを大きくします。toString()
メソッドの実装では、 配列を使用した文字列 を作成するだけです。
_public String toString() {
return new String(_chars);
}
_
他の人が説明したように、StringBuffer
は可変であり、char
配列を使用して実装されます。 StringBuffer
の操作は、インプレース操作です。
詳細については、次のリンクから入手できます http://www.concentric.net/~ttwang/tech/jfastbuf.htm
char
配列を使用した単純なStringBufferの実装を示しています。