&& (AND) และ || (หรือ) ในคำสั่ง IF


142

ฉันมีรหัสต่อไปนี้:

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

ที่partialHitsเป็น HashMap
จะเกิดอะไรขึ้นถ้าคำสั่งแรกเป็นจริง? Java จะยังตรวจสอบคำสั่งที่สองหรือไม่? เพราะในคำสั่งคำสั่งแรกที่จะเป็นจริง HashMap NullPointerExceptionไม่ควรมีคีย์ที่กำหนดดังนั้นหากคำสั่งที่สองคือการตรวจสอบผมจะได้รับ
พูดง่ายๆก็คือถ้าเรามีรหัสต่อไปนี้

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

Java จะตรวจสอบbว่าaเป็นเท็จในกรณีแรกหรือไม่และaเป็นจริงในกรณีที่สองหรือไม่

คำตอบ:


208

ไม่จะไม่ได้รับการประเมิน และมีประโยชน์มาก ตัวอย่างเช่นหากคุณต้องการทดสอบว่า String ไม่เป็น null หรือว่างเปล่าคุณสามารถเขียน:

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

หรือในทางกลับกัน

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

หากเราไม่มี 'short-circuits' ใน Java เราจะได้รับ NullPointerExceptions จำนวนมากในโค้ดด้านบน


1
มีการเปรียบเทียบแบบบิตเพื่อให้คุณสามารถประเมินนิพจน์ทั้งสองได้หรือไม่ เช่น if (str! = null | str.isEmpty ())? (แน่นอนว่านี่ไม่ใช่ตัวอย่างที่ใช้ได้จริง แต่มันโง่ แต่คุณได้ความคิด)
Kezzer

5
ตราบใดที่นิพจน์ไม่มีผลข้างเคียงความหมายของการลัดวงจรจะเทียบเท่ากับการประเมินที่สมบูรณ์ นั่นคือถ้า A เป็นจริงคุณจะรู้ว่า A || B เป็นจริงโดยไม่ต้องประเมิน B ครั้งเดียวที่จะสร้างความแตกต่างคือถ้าการแสดงออกมีผลข้างเคียง สำหรับตัวดำเนินการอื่น ๆ คุณสามารถใช้*และ+เป็นตรรกะandและor; ((A?1:0) * (B?1:0)) == 1, ((A?1:0) + (B?1:0)) > 0. คุณสามารถทำได้xor: ((A?1:0) + (B?1:0)) == 1.
ออก

1
@ Kezzer: เป็นการเปรียบเทียบแบบบิตจริงๆหรือไม่? ฉันคิดว่ามันเป็นตัวดำเนินการboolean(ตรรกะ) มันต่างจากตัวดำเนินการbitwise(จำนวนเต็ม) แม้ว่าจะมีสัญลักษณ์เหมือนกัน
ก็ตาม

4
เคล็ดลับที่มีประโยชน์เมื่อคุณต้องการสลับระหว่าง "&&" และ "||" นิพจน์คือการลบล้างนิพจน์ทั้งหมดในลักษณะที่: !(str != null && !str.isEmpty()) กลายเป็น: (str !(!=) null !(&&) !(!)str.isEmpty()) แล้ว: (str == null || str.isEmpty()) เพราะ: การ !(!=) is == !(&&) is || !(!) eliminates itself ปฏิเสธที่เป็นประโยชน์อื่น ๆ คือ: !(<) is >= !(>) is <= และในทางกลับกัน
egallardo

68

Java มีตัวดำเนินการเปรียบเทียบบูลีน 5 แบบ: &, &&, |, ||, ^

& และ && เป็นตัวดำเนินการ "และ", | และ || "หรือ" ตัวดำเนินการ ^ คือ "xor"

ตัวเดียวจะตรวจสอบทุกพารามิเตอร์โดยไม่คำนึงถึงค่าก่อนที่จะตรวจสอบค่าของพารามิเตอร์ คู่แรกจะตรวจสอบพารามิเตอร์ทางซ้ายและค่าของมันก่อนและถ้าtrue( ||) หรือfalse( &&) ปล่อยให้พารามิเตอร์ที่สองโดยไม่ถูกแตะต้อง รวบรวมเสียง? ตัวอย่างง่ายๆควรทำให้ชัดเจน:

ให้สำหรับตัวอย่างทั้งหมด:

 String aString = null;

และ:

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

พารามิเตอร์ทั้งสองจะถูกตรวจสอบก่อนที่การประเมินจะเสร็จสิ้นและ NullPointerException จะถูกโยนสำหรับพารามิเตอร์ที่สอง

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

พารามิเตอร์แรกจะถูกตรวจสอบและจะส่งคืนfalseดังนั้นพารามิเตอร์ที่สองจะไม่ถูกตรวจสอบเนื่องจากผลลัพธ์ยังfalseคงอยู่

เหมือนกันสำหรับหรือ:

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

จะเพิ่ม NullPointerException ด้วย

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

พารามิเตอร์แรกจะถูกตรวจสอบและจะส่งคืนtrueดังนั้นพารามิเตอร์ที่สองจะไม่ถูกตรวจสอบเนื่องจากผลลัพธ์ยังtrueคงอยู่

XOR ไม่สามารถปรับให้เหมาะสมได้เนื่องจากขึ้นอยู่กับพารามิเตอร์ทั้งสอง


3
"Java มีตัวดำเนินการเปรียบเทียบบูลีนที่แตกต่างกัน 4 ตัว: &, &&, |, ||" ... คุณกำลังลืม^(xor)
aioobe

โอ้ฉันไม่รู้ว่ามันตรวจสอบค่าบูลีนบูลีนด้วย ใช้สำหรับ bitmasks เท่านั้นจนถึงตอนนี้
Hardcoded


20

คำตอบทั้งหมดที่นี่ดีมาก แต่เพื่อแสดงให้เห็นว่าสิ่งนี้มาจากไหนสำหรับคำถามเช่นนี้คุณควรไปที่แหล่งที่มา: ข้อกำหนดภาษา Java

มาตรา 15:23 ตัวดำเนินการตามเงื่อนไขและ (&&)กล่าวว่า:

ตัวดำเนินการ && เป็นเหมือน & (§15.22.2) แต่จะประเมินตัวถูกดำเนินการทางขวามือก็ต่อเมื่อค่าของตัวถูกดำเนินการด้านซ้ายเป็นจริง [... ] ในขณะรันนิพจน์ตัวถูกดำเนินการทางซ้ายมือจะถูกประเมินก่อน [... ] ถ้าค่าผลลัพธ์เป็นเท็จค่าของเงื่อนไขและนิพจน์เป็นเท็จและไม่มีการประเมินนิพจน์ตัวถูกดำเนินการทางขวามือ . หากค่าของตัวถูกดำเนินการด้านซ้ายเป็นจริงนิพจน์ทางขวาจะถูกประเมิน [... ] ค่าผลลัพธ์จะกลายเป็นค่าของนิพจน์เงื่อนไขและนิพจน์ ดังนั้น && จะคำนวณผลลัพธ์เดียวกันกับ & บนบูลีนถูกดำเนินการ มันแตกต่างกันที่นิพจน์ตัวถูกดำเนินการด้านขวาจะได้รับการประเมินตามเงื่อนไขแทนที่จะเป็นเสมอ

และในทำนองเดียวกันมาตรา 15:24 ตัวดำเนินการแบบมีเงื่อนไขหรือ (||)กล่าวว่า:

การ || ตัวดำเนินการเหมือน | (§15.22.2) แต่จะประเมินตัวถูกดำเนินการด้านขวาก็ต่อเมื่อค่าของตัวถูกดำเนินการทางซ้ายมือเป็นเท็จ [... ] ที่รันไทม์นิพจน์ตัวถูกดำเนินการทางซ้ายมือจะถูกประเมินก่อน [... ] ถ้าค่าผลลัพธ์เป็นจริงค่าของเงื่อนไขหรือนิพจน์จะเป็นจริงและนิพจน์ตัวถูกดำเนินการทางขวามือจะไม่ได้รับการประเมิน หากค่าของตัวถูกดำเนินการด้านซ้ายเป็นเท็จระบบจะประเมินนิพจน์ทางขวามือ [... ] ค่าผลลัพธ์จะกลายเป็นค่าของเงื่อนไขหรือนิพจน์ ดังนั้น || คำนวณผลลัพธ์เดียวกันกับ | บนบูลีนหรือบูลีนถูกดำเนินการ มันแตกต่างกันที่นิพจน์ตัวถูกดำเนินการด้านขวาจะถูกประเมินตามเงื่อนไขแทนที่จะเป็นเสมอ

อาจจะซ้ำซาก แต่เป็นการยืนยันที่ดีที่สุดว่ามันทำงานอย่างไร ในทำนองเดียวกันตัวดำเนินการตามเงื่อนไข (? :) จะประเมินเฉพาะ 'ครึ่ง' ที่เหมาะสมเท่านั้น (ครึ่งซ้ายถ้าค่าเป็นจริงครึ่งขวาถ้าเป็นเท็จ) อนุญาตให้ใช้นิพจน์เช่น:

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

โดยไม่มี NullPointerException


6

ไม่ถ้า a เป็นจริง (ในไฟล์ orทดสอบ) b จะไม่ถูกทดสอบเนื่องจากผลลัพธ์ของการทดสอบจะเป็นจริงเสมอไม่ว่าจะเป็นค่าใดของนิพจน์ b

ทำการทดสอบง่ายๆ:

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

จะไม่โยนNullPointerException!


6

การลัดวงจรในที่นี้หมายความว่าเงื่อนไขที่สองจะไม่ได้รับการประเมิน

ถ้า (A && B) จะส่งผลให้ไฟฟ้าลัดวงจรถ้า A เป็นเท็จ

ถ้า (A && B) จะไม่ส่งผลให้เกิดการลัดวงจรหาก A เป็น True

ถ้า (A || B) จะส่งผลให้ไฟฟ้าลัดวงจรถ้า A เป็น True

ถ้า (A || B) จะไม่ส่งผลให้เกิดไฟฟ้าลัดวงจรหาก A เป็นเท็จ


4

ไม่เป็นเช่นนั้น Java จะลัดวงจรและหยุดการประเมินเมื่อรู้ผลลัพธ์


4

ใช่การประเมินการลัดวงจรสำหรับนิพจน์บูลีนเป็นลักษณะการทำงานเริ่มต้นในตระกูล C ทั้งหมด

ข้อเท็จจริงที่น่าสนใจก็คือ Java ยังใช้&และ|เป็นตัวถูกดำเนินการทางลอจิก (มีงานมากเกินไปโดยมีintประเภทที่เป็นการดำเนินการระดับบิตที่คาดไว้) เพื่อประเมินคำศัพท์ทั้งหมดในนิพจน์ซึ่งมีประโยชน์เช่นกันเมื่อคุณต้องการผลข้างเคียง


สิ่งนี้น่าจดจำ: เช่นกำหนดวิธีการ changeData (ข้อมูล) ที่ส่งคืนบูลีนแล้ว: if (a.changeData (data) || b.changeData (data)) {doSomething (); } ไม่เรียกใช้ changeData บน b ถ้า a.changeData () ส่งคืนเป็นจริง แต่ถ้า (a.changeData (data) | b.changeData (data)) {doSomething ()} เรียกใช้ changeData () ทั้งบน a และ b แม้ หากสิ่งที่เรียกใช้ในการส่งคืนจริง
Sampisa

0

สิ่งนี้กลับไปสู่ความแตกต่างพื้นฐานระหว่าง & และ &&, | และ ||

BTW คุณทำงานเดียวกันหลายครั้ง ไม่แน่ใจว่าประสิทธิภาพเป็นปัญหาหรือไม่ คุณสามารถลบบางส่วนของการทำซ้ำได้

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);   
} 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.