Saya memiliki lingkaran yang terlihat seperti ini:
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
Ini adalah konten utama dari metode yang tujuan utamanya adalah mengembalikan array float. Saya ingin metode ini kembali null
jika ada kesalahan, jadi saya meletakkan loop di dalam try...catch
blok, seperti ini:
try {
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
} catch (NumberFormatException ex) {
return null;
}
Tapi kemudian saya juga berpikir untuk meletakkan try...catch
blok di dalam loop, seperti ini:
for (int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
} catch (NumberFormatException ex) {
return null;
}
myFloats[i] = myNum;
}
Apakah ada alasan, kinerja atau sebaliknya, untuk lebih memilih satu daripada yang lain?
Sunting: Konsensus tampaknya lebih bersih untuk meletakkan loop di dalam try / catch, mungkin di dalam metodenya sendiri. Namun, masih ada perdebatan tentang mana yang lebih cepat. Bisakah seseorang menguji ini dan kembali dengan jawaban yang seragam?
java
performance
loops
try-catch
Michael Myers
sumber
sumber
Jawaban:
KINERJA:
Sama sekali tidak ada perbedaan kinerja di mana struktur coba / tangkap ditempatkan. Secara internal, mereka diimplementasikan sebagai tabel rentang kode dalam struktur yang dibuat ketika metode dipanggil. Ketika metode ini dieksekusi, struktur try / catch benar-benar keluar dari gambar kecuali terjadi lemparan, kemudian lokasi kesalahan dibandingkan dengan tabel.
Berikut referensi: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
Tabel dijelaskan sekitar setengah jalan.
sumber
Kinerja : seperti yang dikatakan Jeffrey dalam jawabannya, di Jawa tidak ada banyak perbedaan.
Secara umum , untuk keterbacaan kode, pilihan Anda di mana untuk menangkap pengecualian tergantung pada apakah Anda ingin loop tetap diproses atau tidak.
Dalam contoh Anda, Anda kembali setelah menangkap pengecualian. Dalam hal ini, saya akan mencoba / menangkap loop. Jika Anda hanya ingin menangkap nilai buruk tetapi terus memproses, masukkan ke dalam.
Cara ketiga : Anda selalu bisa menulis metode ParseFloat statis Anda sendiri dan memiliki penanganan pengecualian yang ditangani dalam metode itu daripada loop Anda. Membuat penanganan pengecualian terisolasi ke loop itu sendiri!
sumber
Baiklah, setelah Jeffrey L Whitledge mengatakan bahwa tidak ada perbedaan kinerja (pada tahun 1997), saya pergi dan mengujinya. Saya menjalankan patokan kecil ini:
Saya memeriksa bytecode yang dihasilkan menggunakan javap untuk memastikan bahwa tidak ada yang diuraikan.
Hasil penelitian menunjukkan bahwa, dengan asumsi optimasi JIT tidak signifikan, Jeffrey benar ; sama sekali tidak ada perbedaan kinerja pada Java 6, Sun client VM (Saya tidak memiliki akses ke versi lain). Perbedaan waktu total adalah pada urutan beberapa milidetik selama seluruh tes.
Karena itu, satu-satunya pertimbangan adalah apa yang terlihat paling bersih. Saya menemukan bahwa cara kedua jelek, jadi saya akan tetap pada cara pertama atau cara Ray Hayes .
sumber
Meskipun kinerjanya mungkin sama dan apa yang "terlihat" lebih baik sangat subyektif, masih ada perbedaan fungsionalitas yang cukup besar. Ambil contoh berikut:
Loop sementara berada di dalam blok coba tangkap, variabel 'j' bertambah hingga mencapai 40, dicetak ketika j mod 4 bernilai nol dan pengecualian dilemparkan ketika j mencapai 20.
Sebelum ada detail, berikut ini contoh lainnya:
Logika yang sama seperti di atas, satu-satunya perbedaan adalah bahwa blok try / catch sekarang berada di dalam loop while.
Di sinilah output (saat dalam coba / tangkap):
Dan output lainnya (coba / tangkap sementara):
Di sana Anda memiliki perbedaan yang cukup signifikan:
saat di coba / tangkap keluar dari loop
coba / tangkap sementara loop tetap aktif
sumber
try{} catch() {}
sekitar loop jika loop diperlakukan sebagai satu "unit" dan menemukan masalah dalam unit yang membuat seluruh "unit" (atau loop dalam kasus ini) pergi ke selatan. Masukkantry{} catch() {}
di dalam loop jika Anda memperlakukan iterasi tunggal dari loop, atau elemen tunggal dalam array sebagai "unit" keseluruhan. Jika pengecualian dalam "unit" itu terjadi, kita lewati saja elemen itu dan kemudian kita lanjutkan ke iterasi loop berikutnya.Saya setuju dengan semua posting kinerja dan keterbacaan. Namun, ada kasus di mana itu benar-benar penting. Beberapa orang lain menyebutkan ini, tetapi mungkin lebih mudah untuk melihat dengan contoh.
Pertimbangkan contoh yang sedikit dimodifikasi ini:
Jika Anda ingin metode parseAll () mengembalikan null jika ada kesalahan (seperti contoh asli), Anda akan meletakkan try / catch di luar seperti ini:
Pada kenyataannya, Anda mungkin harus mengembalikan kesalahan di sini alih-alih nol, dan umumnya saya tidak suka memiliki banyak pengembalian, tetapi Anda mendapatkan idenya.
Di sisi lain, jika Anda hanya ingin mengabaikan masalahnya, dan mengurai Strings apa pun yang bisa, Anda akan meletakkan try / catch di bagian dalam loop seperti ini:
sumber
Seperti yang sudah disebutkan, kinerjanya sama. Namun, pengalaman pengguna tidak harus sama. Dalam kasus pertama, Anda akan gagal cepat (yaitu setelah kesalahan pertama), namun jika Anda meletakkan blok coba / tangkap di dalam loop, Anda bisa menangkap semua kesalahan yang akan dibuat untuk panggilan yang diberikan ke metode. Saat mem-parsing array nilai dari string tempat Anda mengharapkan beberapa kesalahan pemformatan, pasti ada kasus di mana Anda ingin dapat menyajikan semua kesalahan kepada pengguna sehingga mereka tidak perlu mencoba dan memperbaikinya satu per satu .
sumber
Jika semuanya gagal, maka format pertama masuk akal. Jika Anda ingin dapat memproses / mengembalikan semua elemen yang tidak gagal, Anda perlu menggunakan formulir kedua. Itu akan menjadi kriteria dasar saya untuk memilih antara metode. Secara pribadi, jika semuanya atau tidak sama sekali, saya tidak akan menggunakan bentuk kedua.
sumber
Selama Anda menyadari apa yang harus Anda capai dalam loop, Anda bisa meletakkan try catch di luar loop. Tetapi penting untuk memahami bahwa perulangan akan berakhir segera setelah pengecualian terjadi dan itu mungkin tidak selalu seperti yang Anda inginkan. Ini sebenarnya kesalahan yang sangat umum dalam perangkat lunak berbasis Java. Orang perlu memproses sejumlah item, seperti mengosongkan antrian, dan secara salah mengandalkan pernyataan coba-coba luar yang menangani semua kemungkinan pengecualian. Mereka juga bisa menangani hanya pengecualian khusus di dalam loop dan tidak mengharapkan pengecualian lain terjadi. Kemudian jika pengecualian terjadi yang tidak ditangani di dalam loop maka loop akan "dipraih", itu mungkin berakhir sebelum waktunya dan pernyataan tangkapan luar menangani pengecualian.
Jika loop memiliki peran dalam kehidupan untuk mengosongkan antrian maka loop itu sangat mungkin bisa berakhir sebelum antrian itu benar-benar dikosongkan. Kesalahan yang sangat umum.
sumber
Dalam contoh Anda tidak ada perbedaan fungsional. Saya menemukan contoh pertama Anda lebih mudah dibaca.
sumber
Anda harus memilih versi luar daripada versi dalam. Ini hanya versi spesifik dari aturan, pindahkan apa pun di luar loop yang dapat Anda pindahkan di luar loop. Bergantung pada kompiler IL dan kompiler JIT, dua versi Anda mungkin atau mungkin tidak berakhir dengan karakteristik kinerja yang berbeda.
Pada catatan lain Anda mungkin harus melihat float.TryParse atau Convert.ToFloat.
sumber
Jika Anda memasukkan try / catch ke dalam loop, Anda akan terus mengulang setelah pengecualian. Jika Anda meletakkannya di luar loop, Anda akan berhenti segera setelah pengecualian dilemparkan.
sumber
Perspektif saya akan mencoba / menangkap blok diperlukan untuk memastikan penanganan pengecualian yang tepat, tetapi membuat blok tersebut memiliki implikasi kinerja. Karena, Loop berisi perhitungan berulang yang intensif, tidak disarankan untuk menempatkan blok percobaan / tangkap di dalam loop. Selain itu, tampaknya di mana kondisi ini terjadi, sering kali "Pengecualian" atau "RuntimeException" yang tertangkap. RuntimeException tertangkap dalam kode harus dihindari. Sekali lagi, jika jika Anda bekerja di perusahaan besar, penting untuk mencatat pengecualian itu dengan benar, atau menghentikan runtime exception untuk terjadi. Inti dari deskripsi ini adalah
PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
sumber
mengatur bingkai stack khusus untuk try / catch menambahkan overhead tambahan, tetapi JVM mungkin dapat mendeteksi fakta bahwa Anda kembali dan mengoptimalkannya.
tergantung pada jumlah iterasi, perbedaan kinerja kemungkinan akan diabaikan.
Namun saya setuju dengan yang lain yang memilikinya di luar loop membuat loop body terlihat lebih bersih.
Jika ada kemungkinan Anda ingin melanjutkan dengan proses alih-alih keluar jika ada nomor yang tidak valid, maka Anda ingin kode berada di dalam loop.
sumber
Jika ada di dalam, maka Anda akan mendapatkan overhead dari struktur try / catch N kali, bukan hanya sekali di luar.
Setiap kali struktur Coba / Tangkap dipanggil, ia menambah biaya overhead untuk pelaksanaan metode. Hanya sedikit kutu memori & prosesor yang diperlukan untuk menangani struktur. Jika Anda menjalankan loop 100 kali, dan demi hipotetis, katakanlah biayanya 1 tick per try / catch call, maka memiliki Try / Catch di dalam loop berharga 100 ticks, dibandingkan dengan hanya 1 tick jika itu di luar loop.
sumber
Inti dari pengecualian adalah untuk mendorong gaya pertama: membiarkan penanganan kesalahan dikonsolidasikan dan ditangani satu kali, tidak segera di setiap situs kesalahan yang mungkin.
sumber
taruh di dalam. Anda dapat terus memproses (jika Anda mau) atau Anda dapat membuang pengecualian yang membantu yang memberi tahu klien nilai myString dan indeks array yang berisi nilai buruk. Saya pikir NumberFormatException sudah akan memberi tahu Anda nilai buruk tetapi prinsipnya adalah untuk menempatkan semua data yang bermanfaat dalam pengecualian yang Anda buang. Pikirkan tentang apa yang akan menarik bagi Anda di debugger pada titik ini dalam program.
Mempertimbangkan:
Pada saat dibutuhkan Anda akan sangat menghargai pengecualian seperti ini dengan sebanyak mungkin informasi di dalamnya.
sumber
Saya ingin menambahkan pendapat saya sendiri
0.02c
tentang dua pertimbangan yang bersaing ketika melihat masalah umum di mana harus menempatkan penanganan pengecualian:"Lebih luas" tanggung jawab
try-catch
blok (yaitu di luar loop dalam kasus Anda) berarti bahwa ketika mengubah kode di beberapa titik kemudian, Anda mungkin keliru menambahkan garis yang ditangani olehcatch
blok Anda yang ada ; mungkin tidak sengaja. Dalam kasus Anda, ini lebih kecil kemungkinannya karena Anda secara eksplisit menangkap aNumberFormatException
Semakin "sempit" tanggung jawab
try-catch
blok, semakin sulit menjadi refactoring. Terutama ketika (seperti dalam kasus Anda) Anda mengeksekusi instruksi "non-lokal" dari dalamcatch
blok (return null
pernyataan).sumber
Itu tergantung pada penanganan kegagalan. Jika Anda hanya ingin melewatkan elemen kesalahan, coba di dalam:
Dalam kasus lain saya lebih suka mencoba di luar. Kode lebih mudah dibaca, lebih bersih. Mungkin akan lebih baik untuk melempar IllegalArgumentException dalam kasus kesalahan daripada mengembalikan null.
sumber
Saya akan memasukkan $ 0,02 saya. Kadang-kadang Anda akhirnya perlu menambahkan "akhirnya" nanti dalam kode Anda (karena siapa yang pernah menulis kode mereka dengan sempurna pertama kali?). Dalam kasus tersebut, tiba-tiba lebih masuk akal untuk mencoba / menangkap di luar loop. Sebagai contoh:
Karena jika Anda mendapatkan kesalahan, atau tidak, Anda hanya ingin melepaskan koneksi basis data Anda (atau memilih jenis sumber daya favorit lainnya ...) satu kali.
sumber
Aspek lain yang tidak disebutkan di atas adalah kenyataan bahwa setiap try-catch memiliki beberapa dampak pada stack, yang dapat memiliki implikasi untuk metode rekursif.
Jika metode "outer ()" memanggil metode "inner ()" (yang dapat menyebut dirinya secara rekursif), cobalah untuk menemukan try-catch dalam metode "outer ()" jika memungkinkan. Contoh "stack crash" sederhana yang kami gunakan di kelas kinerja gagal pada sekitar 6.400 frame ketika try-catch berada di metode dalam, dan sekitar 11.600 saat berada di metode luar.
Di dunia nyata, ini bisa menjadi masalah jika Anda menggunakan pola Komposit dan memiliki struktur bersarang yang besar dan kompleks.
sumber
Jika Anda ingin menangkap Pengecualian untuk setiap iterasi, atau memeriksa di mana Pengecualian iterasi dilemparkan dan menangkap setiap Pengecualian dalam iterasi, tempat coba ... menangkap di dalam loop. Ini tidak akan memutus loop jika Pengecualian terjadi dan Anda dapat menangkap setiap Pengecualian di setiap iterasi sepanjang loop.
Jika Anda ingin memutus perulangan dan memeriksa Pengecualian kapan pun dilemparkan, gunakan coba ... catch out of the loop. Ini akan memutus loop dan menjalankan pernyataan setelah catch (jika ada).
Itu semua tergantung kebutuhan Anda. Saya lebih suka menggunakan try ... tangkap di dalam loop saat menggunakan sebagai, jika Pengecualian terjadi, hasilnya tidak ambigu dan loop tidak akan rusak dan dieksekusi sepenuhnya.
sumber