&& (VÀ) và || (HOẶC) trong các câu lệnh IF


137

Tôi có đoạn mã sau:

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

nơi partialHitslà một HashMap.
Điều gì sẽ xảy ra nếu tuyên bố đầu tiên là đúng? Java vẫn sẽ kiểm tra câu lệnh thứ hai chứ? Bởi vì để câu lệnh đầu tiên là đúng, HashMap không được chứa khóa đã cho, vì vậy nếu câu lệnh thứ hai được chọn, tôi sẽ nhận được NullPointerException.
Vì vậy, nói một cách đơn giản, nếu chúng ta có đoạn mã sau

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

Java sẽ kiểm tra bnếu asai trong trường hợp đầu tiên và nếu ađúng trong trường hợp thứ hai?

Câu trả lời:


202

Không, nó sẽ không được đánh giá. Và điều này rất hữu ích. Ví dụ: nếu bạn cần kiểm tra xem Chuỗi không rỗng hay rỗng, bạn có thể viết:

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

Hoặc cách khác xung quanh

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

Nếu chúng ta không có 'ngắn mạch' trong Java, chúng ta sẽ nhận được rất nhiều NullPulumExceptions trong các dòng mã ở trên.


Có so sánh bitwise tồn tại để bạn có thể đánh giá cả hai biểu thức? tức là nếu (str! = null | str.isEmpty ())? (tất nhiên đây không phải là một ví dụ thực tế, thực tế nó thật ngu ngốc, nhưng bạn hiểu ý)
Kezzer

5
Miễn là các biểu thức không có tác dụng phụ, ngữ nghĩa ngắn mạch tương đương về mặt logic với đánh giá hoàn chỉnh. Nghĩa là, nếu A đúng, bạn biết A || B là đúng mà không phải đánh giá B. Lần duy nhất nó sẽ tạo ra sự khác biệt là nếu một biểu thức có tác dụng phụ. Đối với các toán tử khác, bạn có thể sử dụng *+như logic andor; ((A?1:0) * (B?1:0)) == 1, ((A?1:0) + (B?1:0)) > 0. Bạn thậm chí có thể làm xor: ((A?1:0) + (B?1:0)) == 1.
outis

1
@Kezzer: đó thực sự là so sánh bitwise? Tôi nghĩ rằng nó là một booleantoán tử (logic). Nó khác với bitwisetoán tử (số nguyên), mặc dù có cùng ký hiệu ...
user85421

4
Một mẹo hữu ích khi bạn muốn chuyển đổi giữa '&&' và '||' các biểu thức là để phủ định toàn bộ biểu thức, theo cách: !(str != null && !str.isEmpty()) trở thành: (str !(!=) null !(&&) !(!)str.isEmpty()) và sau đó: (str == null || str.isEmpty()) bởi vì: !(!=) is == !(&&) is || !(!) eliminates itself các phủ định hữu ích khác là: !(<) is >= !(>) is <= và viceversa
egallardo

68

Java có 5 toán tử so sánh boolean khác nhau: &, &&, |, ||, ^

& và && là các toán tử "và", | và || "hoặc" toán tử, ^ là "xor"

Những cái duy nhất sẽ kiểm tra mọi tham số, bất kể giá trị, trước khi kiểm tra giá trị của tham số. Những cái đầu tiên sẽ kiểm tra tham số bên trái và giá trị của nó và nếu true( ||) hoặc false( &&) không để lại tham số thứ hai. Âm thanh tổng hợp? Một ví dụ dễ hiểu cần làm rõ:

Đưa ra cho tất cả các ví dụ:

 String aString = null;

VÀ:

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

Cả hai tham số đều được kiểm tra trước khi đánh giá được thực hiện và NullPulumException sẽ được ném cho tham số thứ hai.

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

Tham số đầu tiên được kiểm tra và nó trả về false, vì vậy tham số thứ hai sẽ không được kiểm tra, vì falsedù sao kết quả cũng vậy.

Tương tự cho OR:

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

Cũng sẽ nâng cao NullPulumException.

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

Tham số đầu tiên được kiểm tra và nó trả về true, vì vậy tham số thứ hai sẽ không được kiểm tra, vì truedù sao kết quả cũng vậy.

XOR không thể được tối ưu hóa, vì nó phụ thuộc vào cả hai tham số.


3
"Java có 4 toán tử so sánh boolean khác nhau: &, &&, |, ||" ... Bạn đang quên ^(xor).
aioobe

Ồ, tôi cũng không biết nó cũng kiểm tra các giá trị boolean. Chỉ sử dụng nó cho bitmasks, cho đến nay.
hardcoded


20

Tất cả các câu trả lời ở đây đều tuyệt vời, nhưng chỉ để minh họa điều này đến từ đâu, đối với các câu hỏi như thế này thì tốt để đi đến nguồn: Đặc tả ngôn ngữ Java.

Mục 15:23, Toán tử có điều kiện và (&&) , cho biết:

Toán tử && giống như & (§15.22.2), nhưng chỉ đánh giá toán hạng bên phải của nó nếu giá trị của toán hạng bên trái của nó là đúng. [...] Trong thời gian chạy, biểu thức toán hạng bên trái được đánh giá đầu tiên [...] nếu giá trị kết quả là sai, giá trị của biểu thức có điều kiện và là sai và biểu thức toán hạng bên phải không được đánh giá . Nếu giá trị của toán hạng bên trái là đúng, thì biểu thức bên phải được ước tính [...] giá trị kết quả trở thành giá trị của biểu thức-và điều kiện. Do đó, && tính kết quả tương tự như & trên các toán hạng boolean. Nó chỉ khác ở chỗ biểu thức toán hạng bên phải được đánh giá có điều kiện chứ không phải luôn luôn.

Và tương tự, Phần 15:24, toán tử có điều kiện-Hoặc (||) , nói:

| | Toán tử giống như | (§15.22.2), nhưng chỉ đánh giá toán hạng bên phải của nó nếu giá trị của toán hạng bên trái của nó là sai. [...] Trong thời gian chạy, biểu thức toán hạng bên trái được đánh giá đầu tiên; [...] nếu giá trị kết quả là đúng, giá trị của biểu thức có điều kiện hoặc là đúng và biểu thức toán hạng bên phải không được đánh giá. Nếu giá trị của toán hạng bên trái là sai, thì biểu thức bên phải được ước tính; [...] Giá trị kết quả trở thành giá trị của biểu thức-hoặc điều kiện. Như vậy, | | tính kết quả tương tự như | trên toán hạng boolean hoặc Boolean. Nó chỉ khác ở chỗ biểu thức toán hạng bên phải được đánh giá có điều kiện chứ không phải luôn luôn.

Một chút lặp đi lặp lại, có thể, nhưng xác nhận tốt nhất về chính xác cách họ làm việc. Tương tự toán tử có điều kiện (? :) chỉ đánh giá 'nửa' thích hợp (nửa bên trái nếu giá trị là đúng, nửa bên phải nếu sai), cho phép sử dụng các biểu thức như:

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

không có NullPulumException.


6

Không, nếu a là đúng (trong một orthử nghiệm), b sẽ không được kiểm tra, vì kết quả của thử nghiệm sẽ luôn đúng, bất kể giá trị của biểu thức b là gì.

Làm một bài kiểm tra đơn giản:

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

sẽ không ném a NullPointerException!


6

Đoản mạch ở đây có nghĩa là điều kiện thứ hai sẽ không được đánh giá.

Nếu (A && B) sẽ dẫn đến đoản mạch nếu A sai.

Nếu (A && B) sẽ không dẫn đến Mạch ngắn nếu A là Đúng.

Nếu (A | | B) sẽ dẫn đến đoản mạch nếu A đúng.

Nếu (A | | B) sẽ không dẫn đến ngắn mạch nếu A sai.


4

Không, nó sẽ không bị lỗi, Java sẽ đoản mạch và ngừng đánh giá một khi nó biết kết quả.


4

Có, việc đánh giá ngắn mạch cho các biểu thức boolean là hành vi mặc định trong tất cả các họ giống như C.

Một thực tế thú vị là Java cũng sử dụng &|như toán hạng logic (họ đang quá tải, với intloại họ là những hoạt động Bitwise dự kiến) để đánh giá tất cả các điều khoản trong biểu thức, mà còn rất hữu ích khi bạn cần có tác dụng phụ.


Điều này rất thú vị để nhớ: ví dụ: đã đưa ra một phương thức changeData (dữ liệu) trả về một boolean, sau đó: if (a.changeData (data) || b.changeData (data)) {doS Something (); } không thực hiện thay đổiData trên b nếu a.changeData () trả về true, nhưng nếu (a.changeData (dữ liệu) | b.changeData (dữ liệu)) {doS Something ()} thực hiện thay đổiData () trên cả a và b, thậm chí nếu một trong những yêu cầu trả về một sự thật trở lại.
Sampisa

0

Điều này quay trở lại sự khác biệt cơ bản giữa & và &&, | và ||

BTW bạn thực hiện các nhiệm vụ tương tự nhiều lần. Không chắc chắn nếu hiệu quả là một vấn đề. Bạn có thể loại bỏ một số trùng lặp.

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);   
} 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.