私は私のプロジェクトで次のようなSQLクエリビルドを見つけました:
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
このStringBuilder
は目的を達成していますか、つまりメモリ使用量を削減していますか?
コンストラクタでは「+」(文字列連結演算子)が使用されているため、私は疑っています。以下のコードのように、Stringを使用するのと同じ量のメモリが必要ですか? ■StringBuilder.append()
を使用する場合は異なります。
return "select id1, " + " id2 " + " from " + " table";
両方のステートメントのメモリ使用量は同じですか?どうか明らかにしてください。
前もって感謝します!
編集:
ところで、それは私のコードではありません。古いプロジェクトで見つけました。また、クエリは私の例のように小さくはありません。 :)
StringBuilderを使用する、つまりメモリを削減する目的。達成されましたか?
いいえ、まったくありません。そのコードはStringBuilder
を正しく使用していません。 (あなたはそれを間違って引用したと思います;確かにid2
とtable
の周りに引用符はありませんか?)
目的は(通常)メモリを減らすことであることに注意してくださいchurn使用されるメモリの合計ではなく、ガベージコレクターでの作業を少し楽にするためです。
それは以下のような文字列を使用するのと同等のメモリを消費しますか?
いいえ、それはあなたが引用した単なる連結よりもmoreメモリチャーンを引き起こします。 (JVMオプティマイザーが、コード内の明示的なStringBuilder
が不要であると判断し、可能であればそれを最適化するまで/を除きます。)
そのコードの作成者がStringBuilder
を使用したい場合(引数がありますが、反対もあります。この回答の最後にある注を参照してください)、適切に実行することをお勧めします(ここでは、実際にid2
の周りに引用符がないと仮定しています) table
):
StringBuilder sb = new StringBuilder(some_appropriate_size);
sb.append("select id1, ");
sb.append(id2);
sb.append(" from ");
sb.append(table);
return sb.toString();
StringBuilder
コンストラクターにsome_appropriate_size
をリストしていることに注意してください。これにより、追加するコンテンツ全体に対して十分な容量で開始されます。指定しない場合に使用されるデフォルトのサイズは 16文字 で、通常は小さすぎるため、StringBuilder
が再割り当てを行って自分自身を大きくする必要があります(IIRC、Sun/Oracle JDK、スペースがなくなるたびに、特定のappend
を満たすためにさらに必要なことがわかっている場合は、それ自体が2倍になります。
文字列の連結willがSun/Oracleコンパイラでコンパイルされている場合、カバーの下でStringBuilder
を使用することを聞いたことがあるかもしれません。これは事実で、式全体に1つのStringBuilder
を使用します。ただし、デフォルトのコンストラクターを使用するため、ほとんどの場合、再割り当てが必要になります。ただし、読みやすいです。これはnot trueのseries連結であることに注意してください。したがって、たとえば、これは1つのStringBuilder
を使用します。
return "prefix " + variable1 + " middle " + variable2 + " end";
おおよそ次のように変換されます。
StringBuilder tmp = new StringBuilder(); // Using default 16 character size
tmp.append("prefix ");
tmp.append(variable1);
tmp.append(" middle ");
tmp.append(variable2);
tmp.append(" end");
return tmp.toString();
デフォルトのコンストラクタと後続の再割り当ては理想的ではありませんが、それで十分です—そして連結はlotより読みやすいです。
しかし、それは単一の式に対してのみです。これには複数のStringBuilder
sが使用されます。
String s;
s = "prefix ";
s += variable1;
s += " middle ";
s += variable2;
s += " end";
return s;
最終的には次のようになります。
String s;
StringBuilder tmp;
s = "prefix ";
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable1);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" middle ");
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable2);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" end");
s = tmp.toString();
return s;
...これはかなりいです。
ただし、ごく少数の場合を除いて重要ではないであり、特定のパフォーマンスの問題がない限り、読みやすさ(保守性を高める)が望ましいことを覚えておくことが重要です。
追加したい「ピース」がすべて揃っている場合、StringBuilder
を使用しても意味がありません。サンプルコードごとに同じ呼び出しでStringBuilder
and文字列連結を使用すると、さらに悪化します。
これは良いでしょう:
return "select id1, " + " id2 " + " from " + " table";
この場合、文字列の連結は実際にはcompile-timeで行われているため、偶数単純なものと同等です:
return "select id1, id2 from table";
new StringBuilder().append("select id1, ").append(" id2 ")....toString()
を使用すると、実際にはhinderのパフォーマンスが低下します。これは、連結を実行時間に強制的に実行するためです、compileの代わりに。おっとっと。
実際のコードがvaluesをクエリに含めることでSQLクエリを構築している場合、それは別のseparate問題です。 SQLではなくパラメーターに値を指定して、パラメーター化されたクエリを使用する必要があります。
String
/StringBuffer
の記事 があります。これは少し前に書いたもので、StringBuilder
が登場する前です。ただし、原則はStringBuilder
にも同じように適用されます。
[[ここにはいくつかの良い答えがありますが、まだ少しの情報が欠けていることがわかります。 ]]
return (new StringBuilder("select id1, " + " id2 " + " from " + " table"))
.toString();
あなたが指摘するように、あなたが与える例は単純化されていますが、とにかくそれを分析しましょう。ここで何が起こるかは、compilerが+
がすべて定数であるため、実際にここで"select id1, " + " id2 " + " from " + " table"
を動作させることです。したがって、これは次のようになります。
return new StringBuilder("select id1, id2 from table").toString();
この場合、明らかに、StringBuilder
を使用しても意味がありません。あなたもそうするかもしれません:
// the compiler combines these constant strings
return "select id1, " + " id2 " + " from " + " table";
ただし、フィールドまたはその他の非定数を追加する場合でも、コンパイラは内部StringBuilder
を使用します-定義する必要はありません。
// an internal StringBuilder is used here
return "select id1, " + fieldName + " from " + tableName;
カバーの下では、これはほぼ次のコードに変わります。
StringBuilder sb = new StringBuilder("select id1, ");
sb.append(fieldName).append(" from ").append(tableName);
return sb.toString();
本当にStringBuilder
直接を使用する必要があるのは、条件付きコードがある場合だけです。たとえば、次のように見えるコードはStringBuilder
にとって必死です:
// 1 StringBuilder used in this line
String query = "select id1, " + fieldName + " from " + tableName;
if (where != null) {
// another StringBuilder used here
query += ' ' + where;
}
最初の行の+
は、1つのStringBuilder
インスタンスを使用します。次に、+=
は別のStringBuilder
インスタンスを使用します。より効率的です:
// choose a good starting size to lower chances of reallocation
StringBuilder sb = new StringBuilder(64);
sb.append("select id1, ").append(fieldName).append(" from ").append(tableName);
// conditional code
if (where != null) {
sb.append(' ').append(where);
}
return sb.toString();
StringBuilder
を使用する別の時間は、いくつかのメソッド呼び出しから文字列を作成するときです。その後、StringBuilder
引数を取るメソッドを作成できます。
private void addWhere(StringBuilder sb) {
if (where != null) {
sb.append(' ').append(where);
}
}
StringBuilder
を使用している場合、+
の使用を同時に監視する必要があります。
sb.append("select " + fieldName);
その+
により、別の内部StringBuilder
が作成されます。もちろんこれは次のようになります。
sb.append("select ").append(fieldName);
最後に、@ T.J.rowderが指摘しているように、StringBuilder
のサイズを常に推測する必要があります。これにより、内部バッファーのサイズを増やすときに作成されるchar[]
オブジェクトの数が節約されます。
文字列ビルダーを使用するという目的は、少なくとも完全には達成されていないと推測するのは正しいことです。
ただし、コンパイラが式"select id1, " + " id2 " + " from " + " table"
を検出すると、実際に舞台裏でStringBuilder
を作成して追加するコードを出力するため、最終的にはそれほど悪い結果にはなりません。
しかし、もちろん、そのコードを見ている人は、それが一種の遅延だと考えるに違いありません。
投稿したコードでは、StringBuilderを誤用しているため、利点はありません。どちらの場合も同じ文字列を作成します。 StringBuilderを使用すると、append
メソッドを使用して、ストリングに対する+
操作を回避できます。次のように使用する必要があります。
return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
Javaでは、文字列型は文字の不変のシーケンスであるため、2つの文字列を追加すると、VMは両方のオペランドが連結された新しい文字列値を作成します。
StringBuilderは可変の文字シーケンスを提供します。これを使用して、新しいStringオブジェクトを作成せずに異なる値または変数を連結できるため、文字列を操作するよりも効率的な場合があります。
これにより、別のメソッド内のパラメーターとして渡されるcharシーケンスのコンテンツを変更するなど、いくつかの便利な機能が提供されます。これは、ストリングでは実行できません。
private void addWhereClause(StringBuilder sql, String column, String value) {
//WARNING: only as an example, never append directly a value to a SQL String, or you'll be exposed to SQL Injection
sql.append(" where ").append(column).append(" = ").append(value);
}
詳細は http://docs.Oracle.com/javase/tutorial/Java/data/buffers.html
MessageFormat も使用できます