Saya sibuk dengan implementasi logika proyek selama hari kerja. Saya punya waktu di hari Sabtu, jadi saya mengeluarkan Thinking In Java versi bahasa Inggris yang tebal dari rak buku dan membaca tentang penyambungan objek string. Buatlah terjemahan dengan mengacu pada buku ini, tambahkan pemikiran Anda sendiri, dan tulis artikel ini untuk mencatatnya.
Objek String yang tidak dapat diubah
Di Java, objek String tidak dapat diubah. Dalam kode, Anda dapat membuat beberapa alias untuk objek String. Tapi semua alias ini merujuk pada hal yang sama.
Misalnya, s1 dan s2 keduanya merupakan alias dari objek "droidyue.com", dan alias tersebut menyimpan referensi ke objek sebenarnya. Jadi s1 = s2
Copy kode kodenya sebagai berikut:
String s1 = "droidyue.com";
Tali s2 = s1;
System.out.println("s1 dan s2 mempunyai referensi yang sama =" + (s1 == s2));
Satu-satunya operator yang kelebihan beban di Java
Di Java, satu-satunya operator yang kelebihan beban terkait dengan penggabungan string. +,+=. Selain itu, desainer Java tidak mengizinkan kelebihan beban pada operator lain.
Analisis penyambungan
Apakah memang ada biaya kinerja?
Setelah memahami dua poin di atas, Anda mungkin memiliki pemikiran ini. Karena objek Sting tidak dapat diubah, penyambungan beberapa (tiga atau lebih) string pasti akan menghasilkan objek String perantara yang berlebihan.
Copy kode kodenya sebagai berikut:
String namapengguna = "Andy";
String umur = "24";
String pekerjaan = "Pengembang";
String info = nama pengguna + umur + pekerjaan;
Untuk mendapatkan info di atas, nama pengguna dan usia akan disambung untuk menghasilkan objek String sementara t1, kontennya adalah Andy24, lalu t1 dan pekerjaan akan disambung untuk menghasilkan objek info akhir yang kita perlukan t1 perantara dihasilkan, dan t1 dibuat. Setelah itu, jika tidak ada daur ulang aktif, maka pasti akan menempati sejumlah ruang. Jika itu adalah penyambungan dari banyak string (dengan asumsi ratusan string, sebagian besar dalam panggilan ke objek toString), biayanya akan lebih besar, dan kinerjanya akan berkurang banyak.
Pemrosesan optimasi kompiler
Apakah memang ada biaya kinerja di atas? Apakah tidak ada optimasi pemrosesan khusus untuk penggabungan string yang biasa digunakan? Jawabannya adalah ya.
Jika suatu program Java ingin dijalankan, maka harus melalui dua periode, yaitu waktu kompilasi dan waktu proses. Selama kompilasi, kompiler Java (Compiler) mengubah file java menjadi bytecode. Saat runtime, Java Virtual Machine (JVM) menjalankan bytecode yang dihasilkan pada waktu kompilasi. Melalui dua periode ini, Java mencapai apa yang disebut kompilasi di satu tempat dan dijalankan di mana saja.
Mari kita bereksperimen dengan optimasi apa yang telah dilakukan selama kompilasi, dan kita dapat membuat sepotong kode yang mungkin memiliki penalti kinerja.
Copy kode kodenya sebagai berikut:
Penggabungan kelas publik {
public static void main(String[] args) {
String namapengguna = "Andy";
String umur = "24";
String pekerjaan = "Pengembang";
String info = nama pengguna + umur + pekerjaan;
Sistem.keluar.println(info);
}
}
Kompilasi Concatenation.java. getConcatenation.kelas
Copy kode kodenya sebagai berikut:
javacConcatenation.java
Kemudian kami menggunakan javap untuk mendekompilasi file Concatenation.class yang telah dikompilasi. javap -c Penggabungan. Jika perintah javap tidak ditemukan, pertimbangkan untuk menambahkan direktori tempat javap berada ke variabel lingkungan atau menggunakan path lengkap ke javap.
Copy kode kodenya sebagai berikut:
17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Penggabungan
Dikompilasi dari "Concatenation.java"
Penggabungan kelas publik {
Penggabungan publik();
Kode:
0: memuat_0
1: invokespecial #1 // Metode java/lang/Object."<init>":()V
4: kembali
public static void main(java.lang.String[]);
Kode:
0: ldc #2 // Tali Andy
2: toko_1
3: ldc #3 // Tali 24
5: toko_2
6: ldc #4 // Pengembang String
8: toko_3
9: baru #5 // kelas java/lang/StringBuilder
12: bodoh
13: invokespecial #6 // Metode java/lang/StringBuilder."<init>":()V
16: memuat_1
17: invokevirtual #7 // Metode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: memuat_2
21: invokevirtual #7 // Metode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: memuat_3
25: invokevirtual #7 // Metode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #8 // Metode java/lang/StringBuilder.toString:()Ljava/lang/String;
31: toko 4
33: getstatic #9 // Bidang java/lang/System.out:Ljava/io/PrintStream;
36: memuat 4
38: invokevirtual #10 // Metode java/io/PrintStream.println:(Ljava/lang/String;)V
41: kembali
}
Diantaranya, ldc, astore, dll. adalah instruksi bytecode Java, mirip dengan instruksi perakitan. Komentar berikut menggunakan konten terkait Java untuk penjelasannya. Kita dapat melihat bahwa ada banyak StringBuilder di atas, tetapi kita tidak memanggilnya secara eksplisit dalam kode Java. Ini adalah optimasi yang dilakukan oleh kompiler Java. Ketika kompiler Java menemukan penyambungan string, itu akan membuat objek StringBuilder, dan berikut ini Penyambungan sebenarnya memanggil metode append dari objek StringBuilder. Dengan begitu, tidak akan ada masalah yang kita khawatirkan di atas.
Optimasi kompiler saja?
Karena compiler sudah melakukan optimasi untuk kita, apakah cukup hanya mengandalkan optimasi compiler saja?
Di bawah ini kita melihat bagian kode yang tidak dioptimalkan dengan kinerja lebih rendah
Copy kode kodenya sebagai berikut:
public void implisitUseStringBuilder(String[] nilai) {
Hasil string = "";
for (int i = 0; i < nilai.panjang; i ++) {
hasil += nilai[i];
}
System.out.println(hasil);
}
Gunakan javac untuk mengkompilasi dan javap untuk melihat
Copy kode kodenya sebagai berikut:
public void implisitUseStringBuilder(java.lang.String[]);
Kode:
0: ldc #11 // String
2: toko_2
3: ikon_0
4: penyimpanan_3
5: iload_3
6: memuat_1
7: panjang larik
8: if_icmpge 38
11: baru #5 // kelas java/lang/StringBuilder
14: bodoh
15: invokespecial #6 // Metode java/lang/StringBuilder."<init>":()V
18: memuat_2
19: invokevirtual #7 // Metode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: memuat_1
23: iload_3
24: banyak sekali
25: invokevirtual #7 // Metode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #8 // Metode java/lang/StringBuilder.toString:()Ljava/lang/String;
31: toko_2
32: termasuk 3, 1
35: pergi ke 5
38: getstatic #9 // Bidang java/lang/System.out:Ljava/io/PrintStream;
41: memuat_2
42: invokevirtual #10 // Metode java/io/PrintStream.println:(Ljava/lang/String;)V
45: kembali
Diantaranya, 8: if_icmpge 38 dan 35: goto 5 membentuk satu lingkaran. 8: if_icmpge 38 berarti jika perbandingan bilangan bulat tumpukan operan JVM lebih besar dari atau sama dengan (hasil kebalikan dari i <values.length), lompat ke baris 38 (System.out). 35: goto 5 artinya melompat langsung ke jalur 5.
Namun satu hal yang sangat penting di sini adalah pembuatan objek StringBuilder terjadi di antara loop, yang berarti berapa banyak objek StringBuilder yang akan dibuat dalam banyak loop, yang jelas tidak baik. Kode tingkat rendah telanjang.
Sedikit pengoptimalan dapat meningkatkan kinerja Anda secara instan.
Copy kode kodenya sebagai berikut:
public void eksplisitUseStringBuider(String[] nilai) {
Hasil StringBuilder = StringBuilder baru();
for (int i = 0; i < nilai.panjang; i ++) {
hasil.tambahkan(nilai[i]);
}
}
Informasi yang dikumpulkan sesuai
Copy kode kodenya sebagai berikut:
public void eksplisitUseStringBuider(java.lang.String[]);
Kode:
0: baru #5 // kelas java/lang/StringBuilder
3: bodoh
4: invokespecial #6 // Metode java/lang/StringBuilder."<init>":()V
7: toko_2
8: ikon_0
9: penyimpanan_3
10: iload_3
11: memuat_1
12: panjang array
13: if_icmpge 30
16: memuat_2
17: memuat_1
18: iload_3
19: banyak sekali
20: invokevirtual #7 // Metode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: muncul
24: termasuk 3, 1
27: pergi ke 10
30: kembali
Seperti terlihat di atas, 13: if_icmpge 30 dan 27: goto 10 membentuk loop, dan 0: new #5 berada di luar loop, jadi StringBuilder tidak akan dibuat berkali-kali.
Secara umum, kita perlu mencoba untuk menghindari pembuatan StringBuilder secara implisit atau eksplisit di badan loop. Oleh karena itu, mereka yang memahami bagaimana kode dikompilasi dan dieksekusi secara internal dapat menulis kode tingkat yang lebih tinggi.
Jika ada kesalahan pada artikel di atas, mohon kritik dan koreksinya.