2011-09-28 31 views
47

Theo Netbeans Gợi ý đặt tên Sử dụng chuỗi các phương pháp .append thay vì nối chuỗiChuỗi StringBuilder.append có hiệu quả hơn chuỗi nối không?

Trông cho nối chuỗi trong các tham số của một lời gọi phương thức append của StringBuilder hoặc StringBuffer.

StringBuilder.append() thực sự hiệu quả hơn nối chuỗi không?

mẫu Mã

StringBuilder sb = new StringBuilder(); 
sb.append(filename + "/"); 

vs

StringBuilder sb = new StringBuilder(); 
sb.append(filename).append("/"); 
+0

Xem nội dung thú vị này: http://www.precisejava.com/javaperf/j2se/StringAndStringBuffer.htm – Zaki

+1

@Zaki: Những điểm chuẩn này thực sự không hữu ích về mặt thực tế. Có gì hữu ích hơn là lập hồ sơ ứng dụng của bạn để xem liệu ứng dụng có thực sự có nút cổ chai hiệu suất hay không. Và chọn các thuật toán và cấu trúc dữ liệu thích hợp cho trường hợp sử dụng cụ thể của bạn. – mellamokb

Trả lời

69

Bạn phải cân bằng khả năng đọc với chức năng.

Hãy nói rằng bạn có những điều sau đây:

String str = "foo"; 
str += "bar"; 
if(baz) str += "baz"; 

này sẽ tạo ra 2 nhà xây dựng chuỗi (nơi bạn chỉ cần 1, thực sự) cộng với một đối tượng chuỗi bổ sung cho thời gian chuyển tiếp. Bạn sẽ hiệu quả hơn nếu bạn đã đi:

StringBuilder strBuilder = new StringBuilder("foo"); 
strBuilder.append("bar"); 
if(baz) strBuilder.append("baz"); 
String str = strBuilder.toString(); 

Nhưng như một vấn đề về phong cách, tôi nghĩ cái đầu tiên trông rất ổn. Lợi ích hiệu suất của việc tạo một đối tượng đơn giản có vẻ rất nhỏ đối với tôi.Bây giờ, nếu thay vì 3 chuỗi, bạn có 10, hoặc 20, hoặc 100, tôi sẽ nói hiệu suất vượt trội hơn phong cách. Nếu nó đã được trong một vòng lặp, chắc chắn tôi muốn sử dụng xây dựng chuỗi, nhưng tôi nghĩ chỉ là một vài dây là tốt để làm 'sloppy' cách để làm cho mã trông sạch hơn. Nhưng ... cái này có một cái bẫy rất nguy hiểm ẩn trong đó! Đọc bên dưới (tạm dừng để xây dựng hồi hộp ... dun dun dunnnn)

Có những người nói luôn sử dụng trình tạo chuỗi rõ ràng. Một lý do là mã của bạn sẽ tiếp tục phát triển, và nó thường sẽ làm như vậy theo cách tương tự như nó đã sẵn sàng (tức là họ sẽ không dành thời gian để cấu trúc lại.) Vì vậy, bạn kết thúc với 10 hoặc 20 câu lệnh đó mỗi lần tạo người xây dựng của riêng họ khi bạn không cần. Vì vậy, để ngăn chặn điều này ngay từ đầu, họ nói luôn sử dụng một người xây dựng rõ ràng. Vì vậy, trong khi trong ví dụ của bạn, nó sẽ không được đặc biệt nhanh hơn, khi ai đó trong tương lai quyết định họ muốn phần mở rộng tập tin ở cuối, hoặc một cái gì đó như thế, nếu họ tiếp tục sử dụng chuỗi nối thay vì StringBuilder, cuối cùng họ sẽ gặp phải vấn đề về hiệu suất.

Chúng tôi cũng cần suy nghĩ về tương lai. Giả sử bạn đang tạo mã Java trở lại trong JDK 1.1 và bạn có phương pháp sau:

public String concat(String s1, String s2, String s3) { 
    return s1 + s2 + s3; 
} 

Lúc đó, nó sẽ chậm vì StringBuilder không tồn tại.

Sau đó, trong JDK 1.3 bạn quyết định làm cho nó nhanh hơn bằng cách sử dụng StringBuffer (StringBuilder vẫn chưa tồn tại). Bạn làm điều này:

public String concat(String s1, String s2, String s3) { 
    StringBuffer sb = new StringBuffer(); 
    sb.append(s1); 
    sb.append(s2); 
    sb.append(s3); 
    return sb.toString(); 
} 

Nó nhanh hơn rất nhiều. Tuyệt vời!

Bây giờ JDK 1.5 đi ra, và cùng với nó đến StringBuilder (đó là nhanh hơn so với StringBuffer) và transation tự động

return s1 + s2 + s3; 

để

return new StringBuilder().append(s1).append(s2).append(s3).toString(); 

Nhưng bạn không có được hiệu suất này vì bạn đang sử dụng StringBuffer một cách rõ ràng. Vì vậy, bằng cách thông minh, bạn đã gây ra một hit hiệu suất khi Java thông minh hơn bạn. Vì vậy, bạn phải ghi nhớ rằng có những điều trên mạng bạn sẽ không nghĩ đến.

+0

@corsiKa: Vòng lặp của tôi chạy 16 lần và đây là những gì tôi đang làm trong vòng lặp - str + = som_value. Của tôi là jdk1.6. Vì vậy, tôi nên sử dụng StringBuilder hay không. – Ashwin

+0

@Ashwin Tôi sẽ sử dụng StringBuilder ở đó, cá nhân. Có cần thiết không? Tôi đoán nó phụ thuộc vào mức độ thường xuyên của vòng lặp được chạy. Nếu bạn đang xây dựng đầu ra của một băm, sau đó bạn có thể mong đợi để làm điều đó rất nhiều. Chắc chắn tốc độ nó lên. Nếu bạn đang xây dựng tiêu đề của chương trình của bạn và nó chỉ được gọi một lần, có lẽ không phải là một việc lớn như vậy. – corsiKa

2

Nó chỉ hiệu quả hơn nếu bạn đang sử dụng rất nhiều nối và chuỗi rất dài. Để sử dụng chung, chẳng hạn như tạo một tên tệp trong ví dụ của bạn, bất kỳ nối chuỗi nào cũng tốt và dễ đọc hơn.

Ở bất kỳ mức nào, phần này của ứng dụng của bạn dường như không phải là nút cổ chai hiệu suất.

+1

+1 nhưng đối tượng StringBuilder tôi nghĩ luôn được tạo – gd1

10

Vâng, ví dụ đầu tiên của bạn là về cơ bản dịch bởi trình biên dịch vào một cái gì đó dọc theo dòng:

StringBuilder sb = new StringBuilder(); 
sb.append(new StringBuilder().append(filename).append("/").toString()); 

nên có, có một thiếu hiệu quả nhất định ở đây. Tuy nhiên, cho dù nó thực sự quan trọng trong chương trình của bạn là một câu hỏi khác. Ngoài việc có phong cách đáng ngờ (gợi ý: chủ quan), nó thường chỉ là vấn đề, nếu bạn đang làm điều này trong một vòng lặp chặt chẽ.

+0

Chuỗi nối có thực sự được thực hiện bằng cách sử dụng 'StringBuilder' không? Nó có vẻ hợp lý hơn khi có 'String' bao gồm một hàm tạo chấp nhận hai chuỗi và nối chúng (có thể cũng có các phiên bản cho 3-4 chuỗi và một chuỗi có một chuỗi' String [] '). Với các chuỗi được nối, người ta có thể thêm chiều dài của họ trước khi phân bổ không gian cho cửa hàng sao lưu của kết quả, và do đó phân bổ chính xác số lượng không gian đúng trong một lần. Nó sẽ có vẻ điên rồ cho trình biên dịch không làm điều đó. – supercat

2

Về mặt lý thuyết, có. Bởi vì các đối tượng String là không thay đổi được: khi được xây dựng, chúng không thể thay đổi được nữa. Vì vậy, bằng cách sử dụng "+" (nối) về cơ bản tạo ra một đối tượng mới mỗi lần.

Thực tế là không. Trình biên dịch đủ thông minh để thay thế tất cả "+" của bạn bằng các phần phụ của StringBuilder.

Đối với một lời giải thích chi tiết hơn: http://kaioa.com/node/59

PS: Netbeans ??? Nào!

+0

'Come on!'? Những gì bạn sẽ giới thiệu sau đó? –

+0

Câu thứ hai của bạn ngụ ý rằng tất cả chúng được đưa vào một StringBuilder. Điều này là không chính xác. Nếu bạn có 'String s =" a "+" b "; s + = "c"; 'bạn nhận được 2 nhà xây dựng chuỗi, không phải 1. – corsiKa

+1

Tôi sẽ recommand hoặc intelliJ hoặc Eclipse – Guillaume

2

Một concat của hai chuỗi nhanh hơn bằng cách sử dụng chức năng này.

Tuy nhiên, nếu bạn có nhiều chuỗi hoặc loại dữ liệu khác nhau, bạn nên sử dụng một StringBuilder hoặc rõ ràng hoặc ngầm. Sử dụng một + với Strings đang sử dụng một StringBuilder ngầm.

5

Không có câu trả lời nào cho đến nay giải quyết rõ ràng trường hợp cụ thể mà gợi ý cho. Nó không nói đến luôn luôn sử dụng StringBuilder#append thay vì nối. Tuy nhiên, nếu bạn đã sử dụng một số StringBuilder, việc kết hợp không hợp lý vì nó tạo ra một số dư StringBuilder (Xem Dirk's answer) và một phiên bản String tạm thời không cần thiết.

Một số câu trả lời đã thảo luận tại sao cách được đề xuất hiệu quả hơn, nhưng điểm chính là, nếu bạn đã có phiên bản StringBuilder, chỉ cần gọi append trên đó. Nó chỉ là có thể đọc được (theo ý kiến ​​của tôi, và dường như bất cứ ai đã viết gợi ý NetBeans) kể từ khi bạn đang gọi append anyway, và nó là một chút hiệu quả hơn.

+0

Có khả năng một số trường hợp mà việc nối đầu tiên sẽ có lợi; hầu hết trong số này là các tình huống trong đó, nếu chuỗi có một số biến thể ngẫu nhiên về độ dài của chúng, thì khả năng kết nối trước sẽ hiệu quả hơn sẽ khá thấp, nhưng với một số kết hợp chuỗi dài đặc biệt, nó có thể "giành được" mọi thời gian. Nếu 'Append' đầu tiên sẽ mở rộng' StringBuilder'/'StringBuffer' thành một kích thước không đủ để chứa thứ hai, sự phân bổ bổ sung gây ra bởi sự nối có thể ít tốn kém hơn mức được sử dụng bởi SB. Một kịch bản hiếm hoi, nhưng có thể. – supercat

+0

@supercat Đó là một điểm thú vị, và một ví dụ tốt về lý do tại sao bạn nên luôn luôn đo lường khi bạn đang tối ưu hóa mã (và sử dụng dữ liệu đại diện). –

+1

Sở thích của riêng tôi là sử dụng quy tắc chính của tôi: "làm điều gì có ý nghĩa". Trong hầu hết các trường hợp, không có sự khác biệt nào về hiệu suất lớn, do đó, hãy ưu tiên ghép nối khi nó rõ ràng hơn và khó có thể ảnh hưởng đến hiệu suất. Trong trường hợp nhiều kết nối phải xuất hiện trong các câu lệnh khác nhau, tôi thích 'StringBuilder' hơn, nhưng khi chúng có thể xuất hiện trong một biểu thức tôi thích điều đó. Trừ khi dây là lớn, tính dễ đọc phải là ưu tiên cao hơn hiệu suất. – supercat

Các vấn đề liên quan