Cấu trúc “if” nào nhanh hơn - câu lệnh hay toán tử bậc ba?


83

Có hai loại ifbáo cáo trong java - cổ điển: if {} else {}và viết tắt: exp ? value1 : value2. Cái này nhanh hơn cái kia hay chúng giống nhau?

tuyên bố:

int x;
if (expression) {
  x = 1;
} else {
  x = 2;
}

toán tử bậc ba:

int x = (expression) ? 1 : 2;

34
Tôi đoán là hoàn toàn không có sự khác biệt. Nó chỉ là cú pháp. Trừ khi trình biên dịch có phần ác (hay cái gì khác) và tôi là sai
sinelaw

4
Bạn đã chuẩn (vi mô) nó? Chia sẻ kết quả.
BalusC

3
Cả hai sẽ nhận được jit'ed. Sẽ không có sự khác biệt nào cả. Và đừng bận tâm đến việc dịch ngược những thứ đó. Điều đầu tiên mà HotSpot làm là loại bỏ tất cả các tối ưu hóa đã được javac áp dụng.
Ivo Wetzel

11
Chúng không tồn tại cho các tốc độ khác nhau. Chúng tồn tại với những mục đích khác nhau. Tôi chắc rằng bạn hiểu sự khác biệt giữa câu lệnh và biểu thức. Các câu lệnh thực hiện các hành động. Biểu thức tạo ra giá trị. iflà để sử dụng trong các câu lệnh. ?là để sử dụng trong các biểu thức.
Mike Dunlavey

3
+1 vì câu trả lời cho câu hỏi này đáng đọc ngay cả khi mục đích của câu hỏi ban đầu bị hướng dẫn sai.
jball

Câu trả lời:


106

Chỉ có một loại câu lệnh "nếu" ở đó. Còn lại là một biểu thức điều kiện. Đối với cái nào sẽ hoạt động tốt hơn: chúng có thể biên dịch sang cùng một mã bytecode và tôi hy vọng chúng sẽ hoạt động giống hệt nhau - hoặc gần đến mức bạn chắc chắn sẽ không muốn chọn cái này hơn cái kia về hiệu suất.

Đôi khi một ifcâu lệnh sẽ dễ đọc hơn, đôi khi toán tử điều kiện sẽ dễ đọc hơn. Đặc biệt, tôi khuyên bạn nên sử dụng toán tử điều kiện khi hai toán hạng đơn giản và không có hiệu ứng phụ, trong khi nếu mục đích chính của hai nhánh tác dụng phụ của chúng, tôi có thể sử dụng một ifcâu lệnh.

Đây là một chương trình mẫu và mã bytecode:

public class Test {
    public static void main(String[] args) {
        int x;
        if (args.length > 0) {
            x = 1;
        } else {
            x = 2;
        }
    }

    public static void main2(String[] args) {
        int x = (args.length > 0) ? 1 : 2;
    }
}

Bytecode được dịch ngược với javap -c Test:

public class Test extends java.lang.Object {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1
       4: return

  public static void main(java.lang.String[]
    Code:
       0: aload_0
       1: arraylength
       2: ifle          10
       5: iconst_1
       6: istore_1
       7: goto          12
      10: iconst_2
      11: istore_1
      12: return

  public static void main2(java.lang.String[
    Code:
       0: aload_0
       1: arraylength
       2: ifle          9
       5: iconst_1
       6: goto          10
       9: iconst_2
      10: istore_1
      11: return
}

Như bạn có thể thấy, có một sự khác biệt nhỏ trong bytecode ở đây - cho dù điều này có istore_1xảy ra trong phạm vi hay không (không giống như nỗ lực cực kỳ thiếu sót trước đây của tôi :) nhưng tôi sẽ rất ngạc nhiên nếu JITter kết thúc với mã gốc khác.


s / có điều kiện tuyên bố / biểu thức điều kiện /
Laurence Gonsalves

1
Tôi đoán bạn không cố ý cho cả hai mainmain2hoàn toàn giống nhau?
ColinD

ấn tượng. Tôi không biết bạn có thể biên dịch mã byte cho đến bây giờ.
Kyle

2
@Kyle: Tôi đã biên dịch Java, sau đó dịch ngược với javap.
Jon Skeet

1
@Kyle: Chính xác. Tôi hầu như mong đợi mã bytecode giống hệt nhau . Vì nó là, nó chỉ là gần như giống hệt :)
Jon Skeet

10

Cả hai ví dụ của bạn có thể sẽ biên dịch thành bytecode giống hệt nhau hoặc gần giống hệt nhau, vì vậy sẽ không có sự khác biệt về hiệu suất.

Nếu có sự khác biệt về tốc độ thực thi, bạn vẫn nên sử dụng phiên bản thành ngữ nhất (sẽ là phiên bản thứ hai để gán một biến duy nhất dựa trên một điều kiện đơn giản và hai biểu thức con đơn giản và phiên bản đầu tiên để thực hiện các hoạt động phức tạp hơn hoặc các hoạt động không phù hợp trên một dòng).


8

Những điều này đều giống nhau. Cả hai đều khá nhanh, thường khoảng 10-30 nano giây. (tùy thuộc vào kiểu sử dụng) Khung thời gian này có quan trọng đối với bạn không?

Bạn nên làm những gì bạn tin là rõ ràng nhất.


4

Chỉ để thêm vào tất cả các câu trả lời khác:

Biểu thức thứ hai thường được gọi là toán tử / câu lệnh bậc ba / bậc ba. Nó có thể rất hữu ích vì nó trả về một biểu thức. Đôi khi nó làm cho mã rõ ràng hơn cho các câu lệnh ngắn điển hình.


4
Ví dụ tuyệt vời về điều này trong thực tế: trong Java, nếu tôi phải tạo chuỗi cuối cùng dựa trên kết quả của một biểu thức, tôi có thể sử dụng cú pháp bậc ba cuối cùng là String whichTable = (Integer.parseInt (clientId)> 500)? "serverClients": "offlineClients"; Sau đó, tôi có thể sử dụng giá trị của biểu thức ở những nơi mà Bảng cần là giá trị cuối cùng. Những điều sau sẽ là bất hợp pháp: final String whichTable = ""; if (Integer.parseInt (clientId)> 500) {whichTable = "serverClients"; } else {whichTable = "offlineClients"; }
James Perih

@JamesPerih Trong trường hợp của một finaltrường, bạn có thể sử dụng các khối khởi tạo để đặt giá trị (mặc dù toán tử có điều kiện trông tốt hơn IMO một tỷ lần) và với các biến cục bộ, bạn có thể gán một giá trị trước khi sử dụng lần đầu tiên sau này trong khối mã bạn Tôi nghĩ rằng trường hợp duy nhất mà một con mối sẽ có lợi thế hơn if-elselà khi gọi super(...)hoặc this(...)bên trong một hàm tạo.
Kröw

3

không - chúng sẽ được biên dịch giống nhau.


0

Toán tử bậc ba nhanh hơn điều kiện if-else.

public class TerinaryTest {
    public static void main(String[] args)
    {
        int j = 2,i = 0;
        Date d1 = new Date();
        for(long l=1;l<100000000;l++)
            if(i==1) j=1;
                else j=0;
        Date d2 = new Date();
        for(long l=1;l<100000000;l++)
            j=i==1?1:0;
        Date d3 = new Date();
        System.out.println("Time for if-else: " + (d2.getTime()-d1.getTime()));
        System.out.println("Time for ternary: " + (d3.getTime()-d2.getTime()));
    }
}

Kết quả kiểm tra:

Đường mòn-1:

Thời gian cho if-else: 63

Thời gian cho bậc ba: 31

Đường mòn-2:

Thời gian cho if-else: 78

Thời gian cho bậc ba: 47

Đường mòn-3:

Thời gian cho if-else: 94

Thời gian cho bậc ba: 31

Đường mòn-4:

Thời gian cho if-else: 78

Thời gian cho bậc ba: 47


Tôi đã có kết quả hoàn toàn ngược lại khi chạy ví dụ của bạn, điều này cho thấy rằng kết quả không đáng tin cậy. Thật không may, bạn đang rơi vào bẫy vi điểm - nổi tiếng là rất khó để thực hiện chính xác vi điểm. Đối với một số ví dụ bạn có thể xem ở đây: stackoverflow.com/questions/2842695/what-is-microbenchmarking
Rogach

Ví dụ cụ thể của bạn gặp phải ít nhất những vấn đề sau: 4 lần thử nghiệm là không đủ, bạn chạy các thử nghiệm luôn theo thứ tự (nếu-khác thứ nhất, bậc ba thứ hai), bạn không khởi động JVM trước khi chạy thử nghiệm, v.v.
Rogach
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.