ตัวดำเนินการลอจิคัล Java ลัดวงจร


102

ชุดใดที่ลัดวงจรและนิพจน์เงื่อนไขเชิงซ้อนนั้นมีการลัดวงจรหมายความว่าอย่างไร

public static void main(String[] args) {
  int x, y, z;

  x = 10;
  y = 20;
  z = 30;

  // T T
  // T F
  // F T
  // F F

  //SET A
  boolean a = (x < z) && (x == x);
  boolean b = (x < z) && (x == z);
  boolean c = (x == z) && (x < z);
  boolean d = (x == z) && (x > z);
  //SET B    
  boolean aa = (x < z) & (x == x);
  boolean bb = (x < z) & (x == z);
  boolean cc = (x == z) & (x < z);
  boolean dd = (x == z) & (x > z);

}

4
ดูคำถามนี้: stackoverflow.com/questions/7101992/…
Eng.Fouad

คำตอบ:


247

&&และ||ผู้ประกอบการ "ลัดวงจร" หมายถึงพวกเขาไม่ได้ประเมินด้านขวามือถ้ามันไม่จำเป็น

ตัวดำเนินการ&และ|เมื่อใช้เป็นตัวดำเนินการเชิงตรรกะจะประเมินทั้งสองด้านเสมอ

มีเพียงกรณีเดียวของการลัดวงจรสำหรับแต่ละโอเปอเรเตอร์และ ได้แก่ :

  • false && ...- ไม่จำเป็นต้องรู้ว่าด้านขวามือคืออะไรเพราะผลลัพธ์จะเป็นได้falseโดยไม่คำนึงถึงค่าที่นั่น
  • true || ...- ไม่จำเป็นต้องรู้ว่าด้านขวามือคืออะไรเพราะผลลัพธ์จะเป็นได้trueโดยไม่คำนึงถึงค่าที่นั่น

ลองเปรียบเทียบพฤติกรรมในตัวอย่างง่ายๆ:

public boolean longerThan(String input, int length) {
    return input != null && input.length() > length;
}

public boolean longerThan(String input, int length) {
    return input != null & input.length() > length;
}

เวอร์ชันที่ 2 ใช้ตัวดำเนินการที่ไม่ลัดวงจร&และจะโยนNullPointerExceptionif inputis nullแต่เวอร์ชันที่ 1 จะส่งคืนfalseโดยไม่มีข้อยกเว้น


9
ฉันแค่อยากจะขยายคำตอบนี้เล็กน้อย ตัวดำเนินการ & = เป็นชวเลขสำหรับ x = x & นิพจน์ดังนั้นจึงไม่ลัดวงจร เช่นเดียวกับตัวดำเนินการ | =
Stormcloud

11
สิ่งหนึ่งที่ฉันอยากจะเน้น | และ & เป็นตัวดำเนินการไบนารีในขณะที่ && และ || เป็นตัวดำเนินการตามเงื่อนไข (เชิงตรรกะ) | และ & ทำงานกับมากกว่าบูลีนในขณะที่ && และ || ทำงานเฉพาะกับบูลีน
ตำนาน

1
พวกเขาไม่เพียง แต่ไม่ประเมินนิพจน์ทางด้านขวามือเท่านั้น แต่โค้ดจะไม่ถูกเรียกใช้เพื่อให้มีสิ่งใดที่ต้องประเมิน นี่เป็นจุดสำคัญในการทำความเข้าใจหากจะเกิดผลข้างเคียง
mckenzm

@mckenzm ความแตกต่างระหว่างการประเมินและดำเนินการคืออะไร?
Kronen

@Kronen Execution อาจส่งผลมากกว่าการประเมินและก่อให้เกิดผลข้างเคียงเช่นข้อยกเว้นหรือความล่าช้าฉันจะจ่ายเงินที่ไม่เกี่ยวข้องกับตัวอย่างนี้
mckenzm

9

SET A ใช้ตัวดำเนินการบูลีนที่ลัดวงจร

ความหมายของ 'การลัดวงจร' ในบริบทของตัวดำเนินการบูลีนคือสำหรับชุดบูลีน b1, b2, ... , bn เวอร์ชันลัดวงจรจะหยุดการประเมินทันทีที่บูลีนตัวแรกเหล่านี้เป็นจริง (|| ) หรือเท็จ (&&)

ตัวอย่างเช่น:

// 2 == 2 will never get evaluated because it is already clear from evaluating
// 1 != 1 that the result will be false.
(1 != 1) && (2 == 2)

// 2 != 2 will never get evaluated because it is already clear from evaluating
// 1 == 1 that the result will be true.
(1 == 1) || (2 != 2)

โปรดระบุว่าเป็นกรณีสำหรับการนี้&&, ||การทำงานแตกต่างกันและจะหยุดการประเมินผลในตัวถูกดำเนินการครั้งแรกซึ่งผลตอบแทนที่แท้จริง;)
FGE

1
ในความเป็นจริงจะเสร็จสมบูรณ์จริงๆทั้งหมด&&, ||, &และ|ประเมินจากซ้ายไปขวา สำหรับชุดบูลีน b1, b2, ... , bn เวอร์ชันลัดวงจรจะหยุดการประเมินเมื่อบูลีนตัวแรกเหล่านี้เป็นจริง ( ||) หรือเท็จ ( &&) บาหลักการอยู่ที่นั่น)
fge

@fge: ใช่คุณพูดถูกแน่นอน คำจำกัดความของคุณแน่นอนกว่าของฉัน ฉันได้อัปเดตคำตอบพร้อมประโยคในความคิดเห็นของคุณแล้ว หวังว่าคงไม่เป็นไร
afrischke

ไม่ต้องกังวลความรู้ไม่มีค่าถ้าไม่แบ่งปัน
fge

4

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

เช่น Expression is: True || เท็จ

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

ในทำนองเดียวกัน False && True

ในกรณีของ && เราต้องการให้ทั้งสองฝ่ายเป็น True ดังนั้นหากด้านซ้ายมือเป็น False ไม่มีจุดใดในการตรวจสอบด้านขวามือคำตอบจะต้องเป็น False และด้วยเหตุนี้จะไม่มีการตรวจสอบเลย


4
boolean a = (x < z) && (x == x);

ชนิดนี้จะลัดวงจรหมายความว่าหาก(x < z)ประเมินเป็นเท็จแล้วไม่ได้รับการประเมินหลังaจะเป็นเท็จมิฉะนั้น&&จะประเมิน(x == x)ด้วย

& เป็นตัวดำเนินการแบบบิต แต่ยังเป็นตัวดำเนินการบูลีน AND ที่ไม่ลัดวงจร

คุณสามารถทดสอบได้จากบางสิ่งดังต่อไปนี้ (ดูจำนวนครั้งที่วิธีการเรียกใช้ในแต่ละกรณี):

public static boolean getFalse() {
    System.out.println("Method");
    return false;
}

public static void main(String[] args) {
    if(getFalse() && getFalse()) { }        
    System.out.println("=============================");        
    if(getFalse() & getFalse()) { }
}

-1 คำตอบของคุณแสดงให้เห็นว่า&เป็นเพียงตัวดำเนินการระดับบิต แต่นั่นไม่เป็นความจริง นอกจากนี้ยังเป็นตัวดำเนินการบูลีน "หรือ"
โบฮีเมีย

@ โบฮีเมียน: ขอบคุณสำหรับหัว true & falseประเมินเป็นเท็จ คุณช่วยอธิบาย "บูลีน" หรือ "ตัวดำเนินการ" ได้ไหม ฉันอาจจะไม่เข้าใจในสิ่งที่คุณพยายามจะพูด
Bhesh Gurung

ขอโทษ - ฉันหมายถึงบูลีนANDไม่ใช่OR! กล่าวtrue & falseคือไวยากรณ์ที่ถูกต้อง -1 ลบ :)
โบฮีเมีย

4

พูดง่ายๆคือการลัดวงจรหมายถึงการหยุดการประเมินเมื่อคุณรู้ว่าคำตอบไม่สามารถเปลี่ยนแปลงได้อีกต่อไป ตัวอย่างเช่นหากคุณกำลังประเมินห่วงโซ่ของตรรกะANDและคุณพบว่าFALSEอยู่ตรงกลางของห่วงโซ่นั้นคุณจะรู้ว่าผลลัพธ์จะเป็นเท็จไม่ว่าค่าของนิพจน์ที่เหลือจะเป็นเท่าใดก็ตาม เช่นเดียวกันกับ chain of ORs: เมื่อคุณค้นพบ a TRUEคุณจะรู้คำตอบทันทีดังนั้นคุณสามารถข้ามการประเมินนิพจน์ที่เหลือได้

คุณแสดงให้ Java ที่คุณต้องการลัดวงจรโดยใช้&&แทน&และแทน|| |ชุดแรกในกระทู้ของคุณไฟฟ้าลัดวงจร

โปรดทราบว่านี่เป็นมากกว่าความพยายามในการประหยัดรอบของ CPU สองสามรอบ: ในนิพจน์เช่นนี้

if (mystring != null && mystring.indexOf('+') > 0) {
    ...
}

การลัดวงจรหมายถึงความแตกต่างระหว่างการทำงานที่ถูกต้องและการขัดข้อง (ในกรณีที่ mystring เป็นโมฆะ)


2

Java มีตัวดำเนินการบูลีนที่น่าสนใจสองตัวที่ไม่พบในภาษาคอมพิวเตอร์อื่น ๆ ส่วนใหญ่ เหล่านี้รุ่นที่สองของ AND และ OR เป็นที่รู้จักในฐานะผู้ประกอบการตรรกะลัดวงจร ดังที่คุณเห็นจากตารางก่อนหน้าตัวดำเนินการ OR จะให้ผลลัพธ์เป็นจริงเมื่อ A เป็นจริงไม่ว่า B จะเป็นอย่างไร

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

ตัวอย่างเช่นโค้ดแฟรกเมนต์ต่อไปนี้แสดงวิธีที่คุณสามารถใช้ประโยชน์จากการประเมินลอจิกลัดวงจรเพื่อให้แน่ใจว่าการดำเนินการแบ่งจะถูกต้องก่อนที่จะประเมิน

if ( denom != 0 && num / denom >10)

เนื่องจากมีการใช้รูปแบบลัดวงจรของ AND ( &&) จึงไม่มีความเสี่ยงที่จะทำให้เกิดข้อยกเว้นรันไทม์จากการหารด้วยศูนย์ หากโค้ดบรรทัดนี้เขียนโดยใช้&AND เวอร์ชันเดียวทั้งสองด้านจะต้องได้รับการประเมินทำให้เกิดข้อยกเว้นรันไทม์เมื่อdenomเป็นศูนย์

เป็นแนวทางปฏิบัติมาตรฐานในการใช้รูปแบบการลัดวงจรของ AND และ OR ในกรณีที่เกี่ยวข้องกับตรรกะบูลีนโดยปล่อยให้เวอร์ชันอักขระเดี่ยวโดยเฉพาะสำหรับการดำเนินการระดับบิต อย่างไรก็ตามมีข้อยกเว้นสำหรับกฎนี้ ตัวอย่างเช่นพิจารณาข้อความต่อไปนี้:

 if ( c==1 & e++ < 100 ) d = 100;

ที่นี่การใช้ตัวเดียว&ช่วยให้มั่นใจได้ว่าการดำเนินการเพิ่มจะถูกนำไปใช้eว่าcจะเท่ากับ 1 หรือไม่


2

Logical OR: - คืนค่าจริงหากตัวถูกดำเนินการอย่างน้อยหนึ่งตัวประเมินเป็นจริง ตัวถูกดำเนินการทั้งสองได้รับการประเมินก่อนใช้ตัวดำเนินการ OR

การลัดวงจรหรือ: - ถ้าตัวถูกดำเนินการด้านซ้ายมือคืนค่าเป็นจริงจะส่งกลับค่าจริงโดยไม่ต้องประเมินตัวถูกดำเนินการด้านขวามือ


2

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

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

(7 == 8) || ( (1 == 3) && (4 == 4))

เฉพาะส่วนที่เน้นเท่านั้นที่จะได้รับการประเมิน ในการคำนวณขั้น||แรกให้ตรวจสอบว่า7 == 8เป็นtrueหรือไม่ ถ้าเป็นเช่นนั้นทางขวามือจะถูกข้ามไปทั้งหมด ด้านขวามือจะตรวจสอบว่า1 == 3เป็นfalseหรือไม่ เพราะมันคือไม่จำเป็นต้องมีการตรวจสอบและประเมินการแสดงออกทั้ง4 == 4 falseถ้าด้านซ้ายมือเป็นtrueเช่น7 == 7แทนที่จะ7 == 8เป็นด้านขวามือทั้งหมดจะถูกข้ามไปเนื่องจาก||นิพจน์ทั้งหมดจะtrueไม่คำนึงถึง

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


1
if(demon!=0&& num/demon>10)

เนื่องจากมีการใช้รูปแบบลัดวงจรของ AND (&&) จึงไม่มีความเสี่ยงที่จะทำให้เกิดข้อยกเว้นเวลาทำงานเมื่อปีศาจเป็นศูนย์

อ้างอิง Java 2 Fifth Edition โดย Herbert Schildt

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