Saya baru saja menemukan beberapa sql query build seperti ini di proyek saya:
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
Apakah ini StringBuilder
mencapai tujuannya, yaitu mengurangi penggunaan memori?
Saya meragukan itu, karena dalam konstruktor '+' (String concat operator) digunakan. Akankah itu mengambil jumlah memori yang sama dengan menggunakan String seperti kode di bawah ini? s Saya mengerti, itu berbeda saat menggunakan StringBuilder.append()
.
return "select id1, " + " id2 " + " from " + " table";
Apakah kedua pernyataan sama dalam penggunaan memori atau tidak? Mohon klarifikasi.
Terima kasih sebelumnya!
Edit:
BTW, itu bukan kode saya . Menemukannya di proyek lama. Juga, kueri tidak sekecil yang ada di contoh saya. :)
java
string
stringbuilder
Vaandu
sumber
sumber
PreparedStatement
atau yang serupa: docs.oracle.com/javase/tutorial/jdbc/basics/prepared.htmlJawaban:
Tidak, tidak sama sekali. Kode itu tidak digunakan
StringBuilder
dengan benar. (Saya pikir Anda salah mengutipnya; pasti tidak ada kutipan di sekitarid2
dantable
?)Perhatikan bahwa tujuannya (biasanya) adalah untuk mengurangi churn memori daripada total memori yang digunakan, untuk membuat hidup sedikit lebih mudah pada pengumpul sampah.
Tidak, ini akan menyebabkan lebih banyak churn memori daripada hanya concat lurus yang Anda kutip. (Hingga / kecuali pengoptimal JVM melihat bahwa eksplisit
StringBuilder
dalam kode tidak diperlukan dan mengoptimalkannya, jika bisa.)Jika penulis kode itu ingin menggunakan
StringBuilder
(ada argumen untuk, tetapi juga menentang; lihat catatan di akhir jawaban ini), lebih baik melakukannya dengan benar (di sini saya berasumsi sebenarnya tidak ada tanda kutipid2
dantable
):StringBuilder sb = new StringBuilder(some_appropriate_size); sb.append("select id1, "); sb.append(id2); sb.append(" from "); sb.append(table); return sb.toString();
Perhatikan bahwa saya telah mendaftar
some_appropriate_size
diStringBuilder
konstruktor, sehingga itu dimulai dengan kapasitas yang cukup untuk konten lengkap yang akan kita tambahkan. Ukuran default yang digunakan jika Anda tidak menentukannya adalah 16 karakter , yang biasanya terlalu kecil dan mengakibatkanStringBuilder
harus melakukan realokasi untuk membuatnya lebih besar (IIRC, di Sun / Oracle JDK, ia menggandakan dirinya sendiri [atau lebih, jika ia tahu bahwa ia membutuhkan lebih banyak untuk memenuhiappend
] tertentu setiap kali kehabisan ruangan).Anda mungkin pernah mendengar bahwa penggabungan string akan menggunakan a di
StringBuilder
bawah sampul jika dikompilasi dengan kompiler Sun / Oracle. Ini benar, ini akan menggunakan satuStringBuilder
untuk ekspresi keseluruhan. Tapi itu akan menggunakan konstruktor default, yang berarti di sebagian besar kasus, itu harus melakukan realokasi. Lebih mudah dibaca. Perhatikan bahwa ini tidak berlaku untuk rangkaian penggabungan. Jadi misalnya, ini menggunakan satuStringBuilder
:return "prefix " + variable1 + " middle " + variable2 + " end";
Secara kasar diterjemahkan menjadi:
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();
Jadi tidak apa-apa, meskipun konstruktor default dan realokasi berikutnya tidak ideal, kemungkinannya cukup bagus - dan penggabungan jauh lebih mudah dibaca.
Tapi itu hanya untuk satu ekspresi. Beberapa
StringBuilder
s digunakan untuk ini:String s; s = "prefix "; s += variable1; s += " middle "; s += variable2; s += " end"; return s;
Itu akhirnya menjadi seperti ini:
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;
... yang sangat jelek.
Namun, penting untuk diingat bahwa dalam semua kecuali beberapa kasus tidak masalah dan menggunakan keterbacaan (yang meningkatkan kemudahan pemeliharaan) lebih disukai kecuali masalah kinerja tertentu.
sumber
x + y + z
ekspresi daripadaStringBuilder
kecuali saya memiliki alasan kuat untuk mencurigai bahwa itu akan menjadi masalah yang signifikan.StringBuilder sql = new StringBuilder(" XXX); sql.append("nndmn");...
.sql.append
Garis serupa ada sekitar 60 baris. Apa ini bagus?StringBuilder
awalnya akan dialokasikan ruang yang cukup untuk string yang Anda berikan kepada konstruktor ditambah 16 karakter. Jadi, jika Anda menambahkan lebih dari 16 karakter (saya berani bilang begitu, jika ada 60 karakter tambahan!), MakaStringBuilder
harus mengalokasikan ulang setidaknya sekali dan mungkin berkali-kali. Jika Anda memiliki gagasan yang masuk akal seberapa besar hasil akhirnya (katakanlah, 400 karakter), yang terbaik adalah melakukansql = new StringBuilder(400);
(atau apa pun) lalu lakukanappend
s.StringBuilder
bahwa sebelumnya akan menghemat sekitar delapan realokasi memori (dengan asumsi string awal sekitar 10 karakter, SB akan menjadi 26 untuk memulai, kemudian digandakan menjadi 52, lalu 104, 208, 416, 832, 1664, 3328, dan terakhir 6656). Hanya penting jika ini adalah hotspot, tetapi tetap saja, jika Anda tahu sebelumnya ... :-)Ketika Anda sudah memiliki semua "bidak" yang ingin Anda tambahkan, tidak ada gunanya menggunakan
StringBuilder
sama sekali. PenggunaanStringBuilder
dan penggabungan string dalam panggilan yang sama sesuai kode sampel Anda bahkan lebih buruk.Ini akan lebih baik:
return "select id1, " + " id2 " + " from " + " table";
Dalam kasus ini, penggabungan string sebenarnya terjadi pada waktu kompilasi , jadi ini setara dengan yang lebih sederhana:
return "select id1, id2 from table";
Penggunaan
new StringBuilder().append("select id1, ").append(" id2 ")....toString()
sebenarnya akan menghambat kinerja dalam kasus ini, karena memaksa penggabungan dilakukan pada waktu eksekusi , bukan pada waktu kompilasi . Ups.Jika kode sebenarnya membangun kueri SQL dengan memasukkan nilai dalam kueri, maka itu adalah pemisah lainnya masalah , yaitu Anda harus menggunakan kueri berparameter, menentukan nilai dalam parameter, bukan di SQL.
Saya memiliki artikel tentang
String
/StringBuffer
yang saya tulis beberapa waktu lalu - sebelumStringBuilder
datang. Prinsip-prinsipnya berlaku denganStringBuilder
cara yang sama.sumber
[[Ada beberapa jawaban yang bagus di sini tetapi saya menemukan bahwa jawaban-jawaban itu masih kurang sedikit informasi. ]]
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")) .toString();
Jadi seperti yang Anda tunjukkan, contoh yang Anda berikan adalah sederhana, tetapi mari kita analisis. Apa yang terjadi di sini adalah kompiler sebenarnya melakukan
+
pekerjaan di sini karena"select id1, " + " id2 " + " from " + " table"
semuanya adalah konstanta. Jadi ini berubah menjadi:return new StringBuilder("select id1, id2 from table").toString();
Dalam hal ini, jelas tidak ada gunanya menggunakan
StringBuilder
. Anda mungkin juga melakukan:// the compiler combines these constant strings return "select id1, " + " id2 " + " from " + " table";
Namun, bahkan jika Anda menambahkan bidang atau non-konstanta apa pun, kompilator akan menggunakan internal
StringBuilder
- Anda tidak perlu mendefinisikannya:// an internal StringBuilder is used here return "select id1, " + fieldName + " from " + tableName;
Di bawah sampulnya, ini berubah menjadi kode yang kira-kira setara dengan:
StringBuilder sb = new StringBuilder("select id1, "); sb.append(fieldName).append(" from ").append(tableName); return sb.toString();
Sungguh satu-satunya saat Anda perlu menggunakan
StringBuilder
secara langsung adalah ketika Anda memiliki kode bersyarat. Misalnya, kode yang terlihat seperti berikut ini sangat membutuhkanStringBuilder
:// 1 StringBuilder used in this line String query = "select id1, " + fieldName + " from " + tableName; if (where != null) { // another StringBuilder used here query += ' ' + where; }
Di
+
baris pertama menggunakan satuStringBuilder
contoh. Kemudian+=
menggunakanStringBuilder
contoh lain . Lebih efisien untuk melakukan:// 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();
Waktu lain yang saya gunakan
StringBuilder
adalah ketika saya membuat string dari sejumlah panggilan metode. Kemudian saya bisa membuat metode yang membutuhkanStringBuilder
argumen:private void addWhere(StringBuilder sb) { if (where != null) { sb.append(' ').append(where); } }
Saat Anda menggunakan
StringBuilder
, Anda harus memperhatikan setiap penggunaan+
pada saat yang sama:sb.append("select " + fieldName);
Itu
+
akan menyebabkan internal lainStringBuilder
dibuat. Ini tentu saja harus:sb.append("select ").append(fieldName);
Terakhir, seperti yang ditunjukkan @TJrowder, Anda harus selalu menebak ukuran file
StringBuilder
. Ini akan menghemat jumlahchar[]
objek yang dibuat saat memperbesar ukuran buffer internal.sumber
Anda benar dalam menebak bahwa tujuan penggunaan pembuat string tidak tercapai, setidaknya tidak sepenuhnya.
Namun, ketika compiler melihat ekspresi
"select id1, " + " id2 " + " from " + " table"
itu memancarkan kode yang sebenarnya membuat diStringBuilder
belakang layar dan menambahkannya, jadi hasil akhirnya tidak terlalu buruk.Tapi tentu saja siapa pun yang melihat kode itu pasti akan berpikir bahwa itu agak terbelakang.
sumber
Dalam kode yang Anda posting tidak akan ada keuntungan, karena Anda menyalahgunakan StringBuilder. Anda membangun String yang sama di kedua kasus. Menggunakan StringBuilder Anda dapat menghindari
+
operasi pada Strings menggunakanappend
metode ini. Anda harus menggunakannya dengan cara ini:return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
Di Java, tipe String adalah urutan karakter yang tidak dapat diubah, jadi ketika Anda menambahkan dua String, VM membuat nilai String baru dengan kedua operan digabungkan.
StringBuilder menyediakan urutan karakter yang bisa berubah, yang dapat Anda gunakan untuk menggabungkan nilai atau variabel yang berbeda tanpa membuat objek String baru, dan karenanya terkadang bisa lebih efisien daripada bekerja dengan string
Ini menyediakan beberapa fitur yang berguna, seperti mengubah konten urutan karakter yang diteruskan sebagai parameter di dalam metode lain, yang tidak dapat Anda lakukan dengan Strings.
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); }
Info lebih lanjut di http://docs.oracle.com/javase/tutorial/java/data/buffers.html
sumber
+
, yang akan diubah menjadi kode yang sama.StringBuilder
berguna saat Anda tidak dapat melakukan semua penggabungan dalam satu ekspresi, tetapi tidak dalam kasus ini.StringBuilder
jika Anda menggunakannyareturn "select id1, " + foo + "something else" + bar;
- jadi mengapa tidak melakukannya? Pertanyaan tersebut tidak memberikan indikasi bahwa ada sesuatu yang perlu diedarkanStringBuilder
.Anda juga bisa menggunakan MessageFormat terlalu
sumber