&& (AND) dan || (OR) dalam pernyataan IF

142

Saya memiliki kode berikut:

if(!partialHits.get(req_nr).containsKey(z) || partialHits.get(req_nr).get(z) < tmpmap.get(z)){  
    partialHits.get(z).put(z, tmpmap.get(z));  
}

dimana partialHitsada HashMap.
Apa yang akan terjadi jika pernyataan pertama benar? Apakah Java masih akan memeriksa pernyataan kedua? Karena agar pernyataan pertama menjadi benar, HashMap tidak boleh berisi kunci yang diberikan, jadi jika pernyataan kedua dicentang, saya akan mendapatkannya NullPointerException.
Jadi dengan kata sederhana, jika kita memiliki kode berikut

if(a && b)  
if(a || b)

akankah Java memeriksa bapakah asalah dalam kasus pertama dan apakah abenar dalam kasus kedua?

Azimut
sumber

Jawaban:

208

Tidak, itu tidak akan dievaluasi. Dan ini sangat berguna. Misalnya, jika Anda perlu menguji apakah String tidak null atau kosong, Anda dapat menulis:

if (str != null && !str.isEmpty()) {
  doSomethingWith(str.charAt(0));
}

atau, sebaliknya

if (str == null || str.isEmpty()) {
  complainAboutUnusableString();
} else {
  doSomethingWith(str.charAt(0));
}

Jika kami tidak memiliki 'sirkuit pendek' di Java, kami akan menerima banyak NullPointerExceptions di baris kode di atas.

Andreas Dolk
sumber
1
Apakah ada perbandingan bitwise sehingga Anda dapat mengevaluasi kedua ekspresi? yaitu jika (str! = null | str.isEmpty ())? (tentu saja ini bukan contoh praktis, sebenarnya itu bodoh, tapi Anda mengerti)
Kezzer
5
Selama ekspresi tidak memiliki efek samping, semantik hubung singkat secara logis setara dengan evaluasi lengkap. Artinya, jika A benar, Anda tahu A || B benar tanpa harus mengevaluasi B. Satu-satunya saat ini akan membuat perbedaan adalah jika ekspresi memiliki efek samping. Adapun operator lain, Anda dapat menggunakan *dan +sebagai logis anddan or; ((A?1:0) * (B?1:0)) == 1, ((A?1:0) + (B?1:0)) > 0. Anda bahkan dapat melakukan xor: ((A?1:0) + (B?1:0)) == 1.
outis
1
@Keer: apakah itu benar-benar perbandingan yang bijak? Saya pikir ini adalah booleanoperator (logis). Ini berbeda dari bitwiseoperator (integer), meskipun memiliki simbol yang sama ...
user85421
4
Trik praktis saat Anda ingin beralih antara '&&' dan '||' ekspresi adalah untuk meniadakan seluruh ekspresi, sedemikian rupa sehingga: !(str != null && !str.isEmpty()) menjadi: (str !(!=) null !(&&) !(!)str.isEmpty()) dan kemudian: (str == null || str.isEmpty()) karena: !(!=) is == !(&&) is || !(!) eliminates itself negasi berguna lainnya adalah: !(<) is >= !(>) is <= dan sebaliknya
egallardo
68

Java memiliki 5 operator perbandingan boolean yang berbeda: &, &&, |, ||, ^

& dan && adalah operator "dan", | dan || "atau" operator, ^ adalah "xor"

Yang tunggal akan memeriksa setiap parameter, terlepas dari nilainya, sebelum memeriksa nilai parameter. Yang ganda pertama-tama akan memeriksa parameter kiri dan nilainya dan if true( ||) atau false( &&) membiarkan yang kedua tidak tersentuh. Terdengar terkompilasi? Contoh mudah harus menjelaskan:

Diberikan untuk semua contoh:

 String aString = null;

DAN:

 if (aString != null & aString.equals("lala"))

Kedua parameter diperiksa sebelum evaluasi selesai dan NullPointerException akan ditampilkan untuk parameter kedua.

 if (aString != null && aString.equals("lala"))

Parameter pertama dicentang dan dikembalikan false, jadi parameter kedua tidak akan diperiksa, karena falsetetap saja hasilnya .

Hal yang sama untuk ATAU:

 if (aString == null | !aString.equals("lala"))

Akan meningkatkan NullPointerException juga.

 if (aString == null || !aString.equals("lala"))

Parameter pertama dicentang dan dikembalikan true, jadi parameter kedua tidak akan diperiksa, karena truetetap saja hasilnya .

XOR tidak dapat dioptimalkan, karena bergantung pada kedua parameter tersebut.

Hardcode
sumber
3
"Java memiliki 4 operator pembanding boolean yang berbeda: &, &&, |, ||" ... Anda lupa ^(xor).
aioobe
Oh, saya tidak tahu itu memeriksa nilai boolean boolean juga. Hanya digunakan untuk bitmask, sejauh ini.
Hardcode
27

Tidak, itu tidak akan diperiksa. Perilaku ini disebut evaluasi sirkuit pendek dan merupakan fitur dalam banyak bahasa termasuk Java.

Peter van der Heijden
sumber
20

Semua jawaban di sini bagus, tetapi hanya untuk menggambarkan dari mana asalnya, untuk pertanyaan seperti ini, sebaiknya lihat sumbernya: Spesifikasi Bahasa Java.

Bagian 15:23, Operator Bersyarat-Dan (&&) , mengatakan:

Operator && seperti & (§15.22.2), tetapi mengevaluasi operan kanannya hanya jika nilai operan kiri benar. [...] Pada waktu proses, ekspresi operan kiri dievaluasi terlebih dahulu [...] jika nilai yang dihasilkan salah, nilai kondisional-dan ekspresi salah dan ekspresi operan kanan tidak dievaluasi . Jika nilai operan kiri benar, maka ekspresi tangan kanan dievaluasi [...] nilai yang dihasilkan menjadi nilai kondisional-dan ekspresi. Jadi, && menghitung hasil yang sama seperti & pada operan boolean. Ini hanya berbeda dalam ekspresi operan kanan dievaluasi secara kondisional daripada selalu.

Dan serupa, Bagian 15:24, Operator Bersyarat-Atau (||) , mengatakan:

Itu || operator seperti | (§15.22.2), tetapi mengevaluasi operan kanannya hanya jika nilai operan kiri salah. [...] Pada waktu proses, ekspresi operan tangan kiri dievaluasi terlebih dahulu; [...] jika nilai yang dihasilkan benar, nilai kondisional-atau ekspresi benar dan ekspresi operan kanan tidak dievaluasi. Jika nilai operan kiri salah, maka ekspresi tangan kanan dievaluasi; [...] nilai yang dihasilkan menjadi nilai kondisional-atau ekspresi. Jadi, || menghitung hasil yang sama dengan | pada operan boolean atau Boolean. Ini hanya berbeda dalam ekspresi operan kanan dievaluasi secara kondisional daripada selalu.

Sedikit berulang, mungkin, tapi konfirmasi terbaik tentang cara kerjanya. Demikian pula operator kondisional (? :) hanya mengevaluasi 'setengah' yang sesuai (setengah kiri jika nilainya benar, setengah kanan jika salah), memungkinkan penggunaan ekspresi seperti:

int x = (y == null) ? 0 : y.getFoo();

tanpa NullPointerException.

Cowan
sumber
6

Tidak, jika a benar (dalam orpengujian), b tidak akan diuji, karena hasil pengujian akan selalu benar, berapapun nilai ekspresi b.

Lakukan tes sederhana:

if (true || ((String) null).equals("foobar")) {
    ...
}

tidak akan melempar NullPointerException!

Romain Linsolas
sumber
6

Hubung singkat di sini berarti kondisi kedua tidak akan dievaluasi.

Jika (A && B) akan mengakibatkan korsleting jika A Salah.

Jika (A && B) tidak akan menghasilkan Sirkuit pendek jika A Benar.

Jika (A || B) akan menghasilkan korsleting jika A Benar.

Jika (A || B) tidak akan mengakibatkan korsleting jika A Salah.

pengguna3211238
sumber
4

Tidak, Java akan mengalami korsleting dan berhenti mengevaluasi setelah mengetahui hasilnya.

abyx
sumber
4

Ya, evaluasi hubung singkat untuk ekspresi boolean adalah perilaku default di semua keluarga mirip-C.

Fakta menarik adalah bahwa Java juga menggunakan operand logika &dan |sebagai (kelebihan beban, dengan inttipe operasi bitwise yang diharapkan) untuk mengevaluasi semua istilah dalam ekspresi, yang juga berguna saat Anda membutuhkan efek samping.

fortran
sumber
Ini menarik untuk diingat: misalnya diberi metode changeData (data) yang mengembalikan boolean, maka: if (a.changeData (data) || b.changeData (data)) {doSomething (); } tidak mengeksekusi changeData pada b jika a.changeData () mengembalikan true, tetapi jika (a.changeData (data) | b.changeData (data)) {doSomething ()} mengeksekusi changeData () pada a dan b, bahkan jika salah satu dipanggil kembali benar.
Sampisa
0

Ini kembali ke perbedaan mendasar antara & dan &&, | dan ||

BTW Anda melakukan tugas yang sama berkali-kali. Tidak yakin apakah efisiensi menjadi masalah. Anda dapat menghapus beberapa duplikasi.

Z z2 = partialHits.get(req_nr).get(z); // assuming a value cannout be null.
Z z3 = tmpmap.get(z); // assuming z3 cannot be null.
if(z2 == null || z2 < z3){   
    partialHits.get(z).put(z, z3);   
} 
Peter Lawrey
sumber