ディレクトリ
はじめに
JDK1.8では、String、StringBuilderおよびStringBufferが文字列の操作に一般的に使用されます。Stringは不変の文字シーケンスであり、StringBuilderとStringBufferは可変文字シーケンスです。StringBuilderはスレッドセーフではなく、StringとStringBuilderはスレッドセーフです。それらの内部実装を分析しましょう。
ストリング
相続
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
ご覧のとおり、StringはSerializable、Comparable、およびCharSequenceインターフェースを実装しています。Serializableは、直列化可能なマークです。Comparableインターフェースには、compareTo()メソッドが含まれています。CharSequenceインターフェースには、charAt()、length()、subSequence()、およびtoString()メソッドが含まれています。
メンバー変数と構築方法
private final char value[];
private int hash; // Default to 0
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
文字列は定数char []配列を使用して文字列を格納します。これは不変です。Char []配列はString型に変換されます。これは、Arrays copyOff()メソッドを介したディープコピーであり、ローカルのchar []配列をコピーして定数char []配列に割り当てます。
StringBuilder和StringBuffer
相続
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
StringBuilderとStringBufferによって実装されるクラスとインターフェースはまったく同じであり、SerializableとCharSquenceインターフェースはStringのように実装されていることがわかります。最も重要なことは、継承されたAbstractStringBuilder抽象クラスがStringBuilderおよびStringBufferのほとんどのメソッドをカプセル化することです。
AbstractStringBuilder
メンバー変数と構築方法
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
AbstractStringBuilderは文字列を格納するchar []配列も定義しますが、finalで変更されないため、文字列は可変です。同時に、タイプがintのグローバル変数coutも定義されています。設定は初期容量の指定することができたときの容量をより良いパフォーマンスを得るためには、。
拡張方法
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
AbstractStringBuilderの展開メソッドは、newCapacity()によって実装されます。この方法では、容量が元の容量の2倍に拡張され(*の代わりにシフト操作が使用されます)、2が追加されます。拡張された容量が指定された容量より少ない場合、minCapacityを最新の容量に設定します。Integerの最大値(0x7fffffff)-8が最新の容量よりも小さい場合は、hugeCapacity()メソッドを呼び出します。オーバーフローするかどうかを判断するには、オーバーフローする場合は例外OutOfMemoryErrorをスローし、そうでない場合は、新しい容量がIntegerの最大値-8より大きい場合は容量をminCapacityに設定し、それ以外の場合はInteger-8の最大値に設定します。Integerの最大値(0x7fffffff)-8が最新の容量より大きい場合、それ以外の場合、容量はnewCapacityに設定されます。
append()メソッド
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
append()メソッドは、StringBuilderおよびStringBufferの最も一般的に使用されるメソッドであり、文字列を追加するための多くのメソッドをオーバーロードします。最初のappend()メソッドは、objオブジェクトをString型に変換し、StringのvalueOf()メソッドを介して追加します。第二のappend()メソッド、場合strがあるヌル、コールappendNull()メソッド、最初の拡張(生容量プラス4ついで)、追加の「N-」、 「U」、 「L」、 「L」 4のキャラクター。場合strがないヌル、最初の拡張(生容量に加えて、文字列の長さ)、コール文字列のGetCharsは()メソッド、STRに加え文字[]配列値を末尾に、そして最終的に能力元の容量+文字列の長さ。オブジェクト自体を返すため、append()メソッドを継続的に呼び出すことができます。
StringBuilder
工法
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
StringBuilderのデフォルトの容量は16であり、その初期容量も指定できることがわかります。StringBuilderオブジェクトに文字列を割り当てる場合、この時点でのStringBuilerの容量は、現在の文字列の長さに16を加えたものになります。
append()メソッド
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilderのappend()メソッドは、抽象クラスAbstractStringBuilderを書き換えて、多くのappend()メソッドをオーバーロードするメソッドです。そのメソッドの実装は、親クラスAbstractStringBuilderのappend()メソッドを呼び出すことです。複数のスレッドがこのメソッドにアクセスする場合、countはグローバル変数であるため、複数のスレッドがcount + = lenを実行すると、countの値に問題が発生し、実際の値と矛盾するため、StringBuilderスレッドは安全ではありません。
toString()メソッド
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
StringBuilderはObjectのtoString()メソッドを書き換え、新しいStirng()は内部的にArraysのcopyOfRange()メソッド(ディープコピー)を呼び出し、新しいStringオブジェクトを返し、元のオブジェクトとメモリ空間を共有しません。
StringBuffer
メンバー変数と構築方法
private transient char[] toStringCache;
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
構築した場合、StringBufferのとStringBuilderのデフォルトとして、容量の大きさの16は、初期容量を指定することができます。唯一の違いは、StringBufferが、transientで装飾されたchar []配列toStringCacheを定義することです。一時的なキーワード。変数を変更する場合、変数はシリアル化できません。
append()メソッド
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
可視のStringBufferとStringBuilderの書き換えなどAbstractStringBuilder親クラスのappend()メソッドは、の親クラスの内部実装呼び出しのappend()メソッド、およびヘビーデューティの数のappend()メソッドを。唯一の違いは、StringBufferがデータを変更するたびに、char []配列toStringCacheが空に設定され、StringBufferの各メソッドの前に、同期されたキーワードが続くことです。複数のスレッドがこのメソッドにアクセスすると、1つのスレッドがこのメソッドにアクセスし、他のスレッドはロックされてこのメソッドにアクセスできなくなります。このスレッドがこのメソッドを実行してロックを解除した場合のみ、他のスレッドがこのメソッドにアクセスできるため、StringBufferスレッドセーフです。
toString()メソッド
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
StringBufferのtoString()メソッドもStringBuilderとは異なります。toStringCacheがnullの場合、最初にキャッシュされ、最後にStringオブジェクトが返されます。StringBufferの新しいString()は、配列のcopyOfRange()メソッドを呼び出さず、単純な配列割り当て(浅いコピー)のみを行い、要素をコピーする時間を節約します。
まとめ
- Stringは不変の文字列で、StringBufferとStringBuilderは可変文字列です。
- StringBuilderはスレッドセーフで高速であり、StringBufferはスレッドセーフで低速です。
- StringBuilderおよびStringBufferのほとんどのメソッドは、親クラスAbstractStringBuilderの実装を呼び出します。拡張メカニズムでは、最初に容量を元の容量の2倍+ 2に増やします。最大容量は、整数の最大値(0x7fffffff)-8です。
- StringBuilderとStringBufferのデフォルトの容量はどちらも16です。StringBufferとStringBuilderを作成するときにサイズを指定して、パフォーマンスを向上させるのに十分な容量がない場合に自動拡張を回避できます。