ตัวดำเนินการทางลัด“ or-assignment” (| =) ใน Java


89

ฉันมีการเปรียบเทียบที่ต้องทำใน Java มานานและฉันอยากรู้ว่าอย่างน้อยหนึ่งอย่างออกมาเป็นจริงหรือไม่ สตริงของการเปรียบเทียบเป็นเวลานานและยากที่จะอ่านดังนั้นฉันโพล่งขึ้นเพื่อให้สามารถอ่านและจะไปใช้ประกอบการทางลัดมากกว่า|=negativeValue = negativeValue || boolean

boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

ฉันคาดว่าnegativeValueจะเป็นจริงหากค่า <something> เริ่มต้นใด ๆ เป็นลบ ใช้ได้หรือไม่ มันจะทำตามที่ฉันคาดหวังหรือไม่? ฉันไม่เห็นมันกล่าวถึงบนไซต์ของ Sun หรือ stackoverflow แต่ดูเหมือนว่า Eclipse จะไม่มีปัญหากับมันและโค้ดจะคอมไพล์และรัน


ในทำนองเดียวกันถ้าฉันต้องการทำการตัดกันเชิงตรรกะหลาย ๆ จุดฉันสามารถใช้&=แทนได้&&หรือไม่?


13
ทำไมคุณไม่ลองดูล่ะ?
โรมัน

นี่คือตรรกะบูลีนทั่วไปไม่ใช่ Java เท่านั้น เพื่อให้คุณสามารถค้นหาสถานที่อื่น ๆ ได้ แล้วทำไมคุณไม่ลองดูล่ะ?
Dykam

16
@Dykam: ไม่มีพฤติกรรมเฉพาะที่นี่ Java สามารถเลือกที่จะทำให้ | = การลัดวงจรเพื่อที่จะไม่ประเมิน RHS หาก LHS เป็นจริงอยู่แล้ว - แต่มันไม่ได้
Jon Skeet

2
@ Jon Skeet: การลัดวงจรจะเหมาะสมสำหรับตัว||=ดำเนินการที่ไม่มีอยู่จริงแต่|=เป็นรูปแบบการรวมกันของบิตหรือตัวดำเนินการ
David Thornley

1
@ Jon Skeet: แน่นอน แต่การ|=ลัดวงจรจะไม่สอดคล้องกับตัวดำเนินการกำหนดสารประกอบอื่น ๆ เนื่องจากa |= b;จะไม่เหมือนกับa = a | b;ข้อแม้ทั่วไปเกี่ยวกับการประเมินaสองครั้ง (ถ้ามีความสำคัญ) สำหรับฉันแล้วดูเหมือนว่าจะไม่มีการตัดสินใจเกี่ยวกับพฤติกรรมทางภาษาที่ยิ่งใหญ่||=ดังนั้นฉันจึงพลาดประเด็นของคุณไป
David Thornley

คำตอบ:


205

|=เป็นผู้ประกอบการที่ได้รับมอบหมาย Punch ( JLS 15.26.2 ) สำหรับผู้ประกอบการตรรกะบูลีน|( JLS 15.22.2 ); เพื่อไม่ให้สับสนกับเงื่อนไขหรือ||( JLS 15.24 ) นอกจากนี้ยังมี&=และ^=สอดคล้องกับเวอร์ชันการกำหนดสารประกอบของตรรกะบูลีน&และ^ตามลำดับ

กล่าวอีกนัยหนึ่งสำหรับboolean b1, b2สองสิ่งนี้เทียบเท่ากัน:

 b1 |= b2;
 b1 = b1 | b2;

ความแตกต่างระหว่างตัวดำเนินการเชิงตรรกะ ( &และ|) เมื่อเทียบกับตัวดำเนินการตามเงื่อนไข ( &&และ||) คือตัวดำเนินการไม่ "shortcircuit"; หลังทำ นั่นคือ:

  • &และประเมินตัวถูกดำเนินการทั้งสอง| เสมอ
  • &&และ||ประเมินผลการดำเนินขวาเงื่อนไข ; ตัวถูกดำเนินการด้านขวาจะได้รับการประเมินก็ต่อเมื่อค่าของมันอาจส่งผลต่อผลลัพธ์ของการดำเนินการไบนารี นั่นหมายความว่าตัวถูกดำเนินการที่ถูกต้องไม่ได้รับการประเมินเมื่อ:
    • ตัวถูกดำเนินการด้านซ้ายของการ&&ประเมินเป็นfalse
      • (เพราะไม่ว่าตัวถูกดำเนินการที่ถูกต้องจะประเมินเป็นอย่างไรนิพจน์ทั้งหมดจะเป็นอย่างไรfalse)
    • ตัวถูกดำเนินการด้านซ้ายของการ||ประเมินเป็นtrue
      • (เพราะไม่ว่าตัวถูกดำเนินการที่ถูกต้องจะประเมินเป็นอย่างไรนิพจน์ทั้งหมดจะเป็นอย่างไรtrue)

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

มีหลายกรณีที่ต้องการการลัดวงจรหรือแม้กระทั่งจำเป็น แต่สถานการณ์ของคุณไม่ใช่หนึ่งในนั้น

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


+1 อย่างละเอียด. ดูเหมือนว่าเป็นไปได้ที่คอมไพเลอร์อาจแปลงเป็นตัวดำเนินการลัดวงจรได้ดีหากสามารถระบุได้ว่า RHS ไม่มีผลข้างเคียง เบาะแสเกี่ยวกับเรื่องนี้หรือไม่?
Carl

ฉันอ่านว่าเมื่อ RHS เป็นเรื่องเล็กน้อยและ SC ไม่จำเป็นจริงๆแล้วตัวดำเนินการ SC ที่ "ฉลาด" จะช้ากว่าเล็กน้อย ถ้าเป็นจริงมันน่าสนใจกว่าที่จะสงสัยว่าคอมไพเลอร์บางตัวสามารถแปลง SC เป็น NSC ได้หรือไม่ในบางสถานการณ์
polygenelubricants

ตัวดำเนินการลัดวงจร @polygenelubricants เกี่ยวข้องกับสาขาบางประเภทภายใต้ประทุนดังนั้นหากไม่มีรูปแบบที่เป็นมิตรกับการทำนายสาขากับค่าความจริงที่ใช้กับตัวดำเนินการและ / หรือสถาปัตยกรรมที่ใช้งานอยู่ไม่มีการทำนายที่ดี / สาขาใด ๆ ( และสมมติว่าคอมไพเลอร์และ / หรือเครื่องเสมือนไม่ได้ทำการเพิ่มประสิทธิภาพใด ๆ ที่เกี่ยวข้องด้วยตนเอง) ใช่แล้วตัวดำเนินการ SC จะแนะนำความช้าบางอย่างเมื่อเทียบกับการไม่ลัดวงจร วิธีที่ดีที่สุดในการค้นหาว่าคอมไพเลอร์ทำอะไรก็ได้คือการคอมไพล์โปรแกรมโดยใช้ SC กับ NSC จากนั้นเปรียบเทียบ bytecode เพื่อดูว่ามันแตกต่างกันหรือไม่
JAB

นอกจากนี้เพื่อไม่ให้สับสนกับค่าที่เหมาะสมหรือผู้ประกอบการซึ่งเป็นยัง |
user253751

16

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

ตามตัวอย่างของความแตกต่างรหัสนี้จะใช้ได้ถ้าtextเป็นโมฆะ:

boolean nullOrEmpty = text == null || text.equals("")

ในขณะที่สิ่งนี้จะไม่:

boolean nullOrEmpty = false;
nullOrEmpty |= text == null;
nullOrEmpty |= text.equals(""); // Throws exception if text is null

(เห็นได้ชัดว่าคุณสามารถทำได้"".equals(text)สำหรับกรณีนั้น - ฉันแค่พยายามแสดงหลักการ)


3

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

boolean negativeValue
    = defaultStock < 0 
    | defaultWholesale < 0
    | defaultRetail < 0
    | defaultDelivery < 0;

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


ฉันเริ่มต้นด้วยเพียงข้อเดียว แต่ตามที่ระบุไว้ในคำถามเดิมฉันรู้สึกว่า "สตริงของการเปรียบเทียบนั้นยาวและอ่านยากดังนั้นฉันจึงเลิกใช้เพื่อให้อ่านง่าย" นอกจากนี้ในกรณีนี้ฉันสนใจที่จะเรียนรู้พฤติกรรมของ | = มากกว่าการทำให้โค้ดชิ้นนี้ใช้งานได้
David Mason

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

1
@ ฟาริดบางครั้งคุณไม่ต้องการหยุดพูดว่าฉันต้องการตรวจสอบว่ามีงานจำนวนมากที่ทำอะไรบางอย่างหรือไม่ ฉันมีbusyธงเพียงเพราะงานหนึ่งของฉันทำบางอย่างไม่ได้หมายความว่าฉันไม่ต้องการทำงานอื่น ๆ แต่ถ้าไม่มีงานยุ่งฉันต้องการดำเนินการแทน
Peter Lawrey

1

แม้ว่ามันอาจจะมากเกินไปสำหรับปัญหาของคุณ แต่ห้องสมุดGuavaมีไวยากรณ์ที่ดีกับPredicates และทำการประเมินการลัดวงจรของหรือ / และPredicates

โดยพื้นฐานแล้วการเปรียบเทียบจะกลายเป็นวัตถุบรรจุลงในคอลเลกชั่นจากนั้นจึงวนซ้ำ สำหรับหรือเพรดิเคต Hit จริงแรกจะกลับมาจากการวนซ้ำและในทางกลับกันสำหรับและ


1

หากเป็นเรื่องเกี่ยวกับความสามารถในการอ่านฉันมีแนวคิดในการแยกข้อมูลที่ทดสอบจากตรรกะการทดสอบ ตัวอย่างโค้ด:

// declare data
DataType [] dataToTest = new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
}

// define logic
boolean checkIfAnyNegative(DataType [] data) {
    boolean negativeValue = false;
    int i = 0;
    while (!negativeValue && i < data.length) {
        negativeValue = data[i++] < 0;
    }
    return negativeValue;
}

โค้ดดูละเอียดและอธิบายได้มากขึ้น คุณสามารถสร้างอาร์เรย์ในการเรียกใช้เมธอดได้เช่นนี้:

checkIfAnyNegative(new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
});

อ่านได้ง่ายกว่า 'สตริงเปรียบเทียบ' และยังมีข้อได้เปรียบด้านประสิทธิภาพของการลัดวงจร (โดยเสียค่าใช้จ่ายในการจัดสรรอาร์เรย์และการเรียกใช้เมธอด)

แก้ไข: สามารถอ่านได้ง่ายขึ้นโดยใช้พารามิเตอร์ varargs:

ลายเซ็นวิธีจะเป็น:

boolean checkIfAnyNegative(DataType ... data)

และการโทรอาจมีลักษณะดังนี้:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

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

@DavidMason ฉันเห็นด้วย อย่างไรก็ตามโปรดจำไว้ว่าเครื่องคิดเลขสมัยใหม่ส่วนใหญ่จะกลืนค่าใช้จ่ายประเภทนั้นในเวลาน้อยกว่าไม่กี่มิลลิวินาที ส่วนตัวผมจะไม่สนใจเกี่ยวกับค่าใช้จ่ายจนกว่าปัญหาประสิทธิภาพการทำงานซึ่งดูเหมือนจะเป็นที่เหมาะสม นอกจากนี้การใช้คำฟุ่มเฟือยของโค้ดก็เป็นข้อดีโดยเฉพาะอย่างยิ่งเมื่อ JAutodoc ไม่ได้จัดเตรียมหรือสร้าง javadoc
Krzysztof Jabłoński

1

เป็นกระทู้เก่า แต่เพื่อให้มุมมองที่แตกต่างสำหรับผู้เริ่มต้นฉันขอยกตัวอย่าง

+=ผมคิดว่าส่วนใหญ่กรณีการใช้งานทั่วไปสำหรับผู้ประกอบการสารคล้ายจะเป็น ฉันแน่ใจว่าเราทุกคนเขียนสิ่งนี้:

int a = 10;   // a = 10
a += 5;   // a = 15

จุดนี้คืออะไร? ประเด็นคือหลีกเลี่ยงการสร้างสำเร็จรูปและกำจัดรหัสซ้ำ

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

b1 |= b2;

1
เป็นการยากที่จะสังเกตเห็นข้อผิดพลาดในการดำเนินการและชื่อตัวดำเนินการโดยเฉพาะอย่างยิ่งเมื่อชื่อยาว longNameOfAccumulatorAVariable += 5; เทียบกับ longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;
Andrei

0
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;

ฉันคิดว่าฉันชอบnegativeValue = defaultStock < 0 || defaultWholesale < 0ฯลฯ นอกเหนือจากความไม่มีประสิทธิภาพของการชกมวยและการห่อทั้งหมดที่เกิดขึ้นที่นี่ฉันไม่พบว่ามันง่ายอย่างที่จะเข้าใจว่าโค้ดของคุณหมายถึงอะไร
Jon Skeet

ฉันมีพารามิเตอร์มากกว่า 4 ตัวและเกณฑ์ก็เหมือนกันสำหรับทุกตัวฉันชอบวิธีแก้ปัญหาของฉัน แต่เพื่อประโยชน์ในการอ่านฉันจะแยกมันออกเป็นหลายบรรทัด (เช่นสร้างรายการค้นหาค่าต่ำสุดเปรียบเทียบค่าต่ำสุดกับ 0) .
โรมัน

สิ่งนี้ดูมีประโยชน์สำหรับการเปรียบเทียบจำนวนมาก ในกรณีนี้ฉันคิดว่าการลดความชัดเจนไม่คุ้มค่ากับการพิมพ์ ฯลฯ
David Mason

0

|| บูลีนตรรกะหรือ
| บิตหรือ

| = bitwise รวม OR และตัวดำเนินการกำหนด

สาเหตุที่ | = ไม่ shortcircit เป็นเพราะมันทำแบบบิตหรือไม่ใช่ตรรกะหรือ กล่าวคือ:

C | = 2 เหมือนกับ C = C | 2

บทช่วยสอนสำหรับตัวดำเนินการ java


ไม่มีบิตหรือบูลีน! ตัวดำเนินการ|เป็นจำนวนเต็มบิตตามหรือแต่ยังเป็นตรรกะ OR - ดู ข้อกำหนดภาษา Java 15.22.2
user85421

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