Tôi có nên sử dụng String.format () của Java nếu hiệu suất là quan trọng?


215

Chúng tôi phải xây dựng Chuỗi mọi lúc cho đầu ra nhật ký, v.v. Qua các phiên bản JDK, chúng tôi đã biết khi nào nên sử dụng StringBuffer(nhiều phụ lục, an toàn luồng) và StringBuilder(nhiều phụ lục, không an toàn luồng).

Lời khuyên về việc sử dụng là String.format()gì? Là nó hiệu quả, hay chúng ta buộc phải gắn bó với việc ghép một lớp trong đó hiệu suất là quan trọng?

ví dụ như kiểu cũ xấu xí,

String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";

so với phong cách mới gọn gàng (String.format, có thể chậm hơn),

String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);

Lưu ý: trường hợp sử dụng cụ thể của tôi là hàng trăm chuỗi nhật ký 'một lớp' trong toàn bộ mã của tôi. Chúng không liên quan đến một vòng lặp, vì vậy StringBuilderquá nặng. Tôi String.format()đặc biệt quan tâm .


28
Tại sao bạn không kiểm tra nó?
Ed S.

1
Nếu bạn đang tạo ra sản phẩm này, thì tôi cho rằng nó phải được con người đọc được theo tỷ lệ mà con người có thể đọc được. Hãy nói tối đa 10 dòng mỗi giây. Tôi nghĩ bạn sẽ thấy nó thực sự không quan trọng bằng cách tiếp cận nào, nếu nó chậm hơn về mặt ý nghĩa, người dùng có thể đánh giá cao nó. ;) Vì vậy, StringBuilder không nặng trong hầu hết các tình huống.
Peter Lawrey

9
@Peter, không, nó hoàn toàn không dành cho con người đọc trong thời gian thực! Nó ở đó để giúp phân tích khi mọi thứ đi sai. Đầu ra nhật ký thường sẽ là hàng ngàn dòng mỗi giây, vì vậy nó cần phải hiệu quả.
Không khí

5
nếu bạn đang tạo ra hàng nghìn dòng mỗi giây, tôi sẽ đề xuất 1) sử dụng văn bản ngắn hơn, thậm chí không có văn bản nào như CSV đơn giản hoặc nhị phân 2) Không sử dụng Chuỗi, bạn có thể ghi dữ liệu vào ByteBuffer mà không cần tạo bất kỳ đối tượng nào (dưới dạng văn bản hoặc nhị phân) 3) làm nền cho việc ghi dữ liệu vào đĩa hoặc ổ cắm. Bạn sẽ có thể duy trì khoảng 1 triệu dòng mỗi giây. (Về cơ bản là nhiều như hệ thống con đĩa của bạn sẽ cho phép) Bạn có thể đạt được các đợt gấp 10 lần này.
Peter Lawrey

7
Điều này không liên quan đến trường hợp chung, nhưng để đăng nhập cụ thể, LogBack (được viết bởi tác giả Log4j gốc) có một hình thức ghi nhật ký được tham số hóa để giải quyết vấn đề chính xác này - logback.qos.ch/manual/arch architecture.html#ParametrienedLogging
Matt Passell

Câu trả lời:


122

Tôi đã viết một lớp nhỏ để kiểm tra có hiệu suất tốt hơn của cả hai và + đi trước định dạng. theo hệ số từ 5 đến 6. Hãy tự thử

import java.io.*;
import java.util.Date;

public class StringTest{

    public static void main( String[] args ){
    int i = 0;
    long prev_time = System.currentTimeMillis();
    long time;

    for( i = 0; i< 100000; i++){
        String s = "Blah" + i + "Blah";
    }
    time = System.currentTimeMillis() - prev_time;

    System.out.println("Time after for loop " + time);

    prev_time = System.currentTimeMillis();
    for( i = 0; i<100000; i++){
        String s = String.format("Blah %d Blah", i);
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

    }
}

Chạy ở trên cho N khác nhau cho thấy cả hai hoạt động tuyến tính, nhưng String.formatchậm hơn 5-30 lần.

Lý do là trong triển khai hiện tại String.formattrước tiên phân tích cú pháp đầu vào bằng các biểu thức thông thường và sau đó điền vào các tham số. Nối với cộng, mặt khác, được tối ưu hóa bằng javac (không phải do JIT) và sử dụng StringBuilder.appendtrực tiếp.

So sánh thời gian chạy


12
Có một lỗ hổng với thử nghiệm này ở chỗ nó không hoàn toàn là một đại diện tốt cho tất cả các định dạng chuỗi. Thường có logic liên quan đến những gì cần bao gồm và logic để định dạng các giá trị cụ thể thành các chuỗi. Bất kỳ thử nghiệm thực tế nên nhìn vào các kịch bản trong thế giới thực.
Orion Adrian

9
Có một câu hỏi khác về SO về + câu StringBuffer, trong các phiên bản gần đây của Java + đã được thay thế bằng StringBuffer khi có thể để hiệu suất sẽ không khác
hhafez

25
Điều này trông rất giống với loại microbenchmark sẽ được tối ưu hóa đi một cách rất khó tin.
David H. Clements

20
Một tiêu chuẩn vi mô kém được thực hiện. Làm thế nào để cả hai phương pháp quy mô theo thứ tự độ lớn. Cách sử dụng, 100, 1000, 10000, 1000000, hoạt động. Nếu bạn chỉ chạy một thử nghiệm, trên một mức độ lớn, trên một ứng dụng không chạy trên lõi bị cô lập; không có cách nào để biết mức độ khác biệt có thể được ghi là "tác dụng phụ" do chuyển đổi bối cảnh, quá trình nền, v.v.
Evan Plaice

8
Hơn nữa, vì bạn không bao giờ thoát ra khỏi JIT chính không thể tham gia.
Jan Zyka

241

Tôi lấy mã hhafez và thêm một bài kiểm tra bộ nhớ :

private static void test() {
    Runtime runtime = Runtime.getRuntime();
    long memory;
    ...
    memory = runtime.freeMemory();
    // for loop code
    memory = memory-runtime.freeMemory();

Tôi chạy riêng điều này cho từng cách tiếp cận, toán tử '+', String.format và StringBuilder (gọi toString ()), vì vậy bộ nhớ được sử dụng sẽ không bị ảnh hưởng bởi các cách tiếp cận khác. Tôi đã thêm nhiều cách nối, tạo chuỗi thành "Blah" + i + "Blah" + i + "Blah" + i + "Blah".

Kết quả như sau (trung bình 5 lần chạy mỗi lần):
Thời gian tiếp cận (ms) Bộ nhớ được phân bổ (dài)
'+' toán tử 747 320.504
String.format 16484 373.312
StringBuilder 769 57.344

Chúng ta có thể thấy rằng String '+' và StringBuilder thực tế giống hệt nhau theo thời gian, nhưng StringBuilder sử dụng bộ nhớ hiệu quả hơn nhiều. Điều này rất quan trọng khi chúng ta có nhiều lệnh gọi nhật ký (hoặc bất kỳ câu lệnh nào khác liên quan đến chuỗi) trong một khoảng thời gian đủ ngắn để Trình thu gom rác sẽ không xóa sạch nhiều trường hợp chuỗi kết quả của toán tử '+'.

Và một lưu ý, BTW, đừng quên kiểm tra mức ghi nhật ký trước khi xây dựng thông báo.

Kết luận:

  1. Tôi sẽ tiếp tục sử dụng StringBuilder.
  2. Tôi có quá nhiều thời gian hoặc quá ít cuộc sống.

8
"Đừng quên kiểm tra mức ghi nhật ký trước khi xây dựng thông báo", là một lời khuyên tốt, điều này ít nhất nên được thực hiện cho các thông báo gỡ lỗi, bởi vì có thể có rất nhiều trong số chúng và chúng không nên được kích hoạt trong sản xuất.
stivlo

39
Không, điều này không đúng. Xin lỗi để được thẳng thừng nhưng số lượng upvote nó đã thu hút là không có gì đáng báo động. Sử dụng +toán tử biên dịch thành StringBuildermã tương đương . Microbenchmark như thế này không phải là một cách tốt để đo lường hiệu suất - tại sao không sử dụng jvisualvm, nó có trong jdk vì một lý do. String.format() sẽ chậm hơn, nhưng do thời gian phân tích chuỗi định dạng chứ không phải phân bổ đối tượng. Trì hoãn việc tạo ra các tạo tác ghi nhật ký cho đến khi bạn chắc chắn rằng chúng cần thiết lời khuyên tốt, nhưng nếu nó có ảnh hưởng đến hiệu suất thì nó đã ở sai vị trí.
RèmDog

1
@CurtainDog, nhận xét của bạn đã được thực hiện trên một bài đăng bốn năm tuổi, bạn có thể chỉ vào tài liệu hoặc tạo một câu trả lời riêng để giải quyết sự khác biệt không?
kurtzbot

1
Tham khảo để hỗ trợ bình luận của @ RèmDog: stackoverflow.com/a/1532499/2872712 . Đó là, + được ưa thích trừ khi nó được thực hiện trong một vòng lặp.
hoa mai

And a note, BTW, don't forget to check the logging level before constructing the message.không phải là lời khuyên tốt. Giả sử chúng ta đang nói về java.util.logging.*cụ thể, kiểm tra mức ghi nhật ký là khi bạn nói về việc xử lý nâng cao sẽ gây ra tác dụng phụ cho chương trình mà bạn không muốn khi chương trình không đăng nhập được bật ở mức phù hợp. Định dạng chuỗi không phải là kiểu xử lý AT ALL. Định dạng là một phần của java.util.loggingkhung công tác và chính trình ghi nhật ký sẽ kiểm tra mức ghi nhật ký trước khi trình định dạng được gọi.
searchengine27

30

Tất cả các điểm chuẩn được trình bày ở đây có một số sai sót , do đó kết quả không đáng tin cậy.

Tôi đã ngạc nhiên khi không ai sử dụng JMH cho điểm chuẩn, vì vậy tôi đã làm.

Các kết quả:

Benchmark             Mode  Cnt     Score     Error  Units
MyBenchmark.testOld  thrpt   20  9645.834 ± 238.165  ops/s  // using +
MyBenchmark.testNew  thrpt   20   429.898 ±  10.551  ops/s  // using String.format

Đơn vị là hoạt động mỗi giây, càng nhiều càng tốt. Mã nguồn chuẩn . Máy ảo Java OpenJDK IcedTea 2.5.4 đã được sử dụng.

Vì vậy, kiểu cũ (sử dụng +) nhanh hơn nhiều.


5
Điều này sẽ dễ hiểu hơn nhiều nếu bạn chú thích "+" và "định dạng".
AjahnCharles

21

Phong cách xấu xí cũ của bạn được JAVAC 1.6 tự động biên dịch thành:

StringBuilder sb = new StringBuilder("What do you get if you multiply ");
sb.append(varSix);
sb.append(" by ");
sb.append(varNine);
sb.append("?");
String s =  sb.toString();

Vì vậy, hoàn toàn không có sự khác biệt giữa điều này và sử dụng StringBuilder.

String.format nặng hơn rất nhiều vì nó tạo ra một Formatter mới, phân tích chuỗi định dạng đầu vào của bạn, tạo StringBuilder, nối thêm mọi thứ vào nó và gọi toString ().


Về khả năng đọc, mã bạn đã đăng còn ... rườm rà hơn String.format ("Bạn nhận được gì nếu nhân% d với% d?", VarSix, varNine);
dusktreader

12
Không có sự khác biệt giữa +StringBuilderthực sự. Thật không may, có rất nhiều thông tin sai trong các câu trả lời khác trong chủ đề này. Tôi gần như bị cám dỗ để thay đổi câu hỏi thành how should I not be measuring performance.
RèmDog

12

String.format của Java hoạt động như vậy:

  1. nó phân tích chuỗi định dạng, phát nổ thành một danh sách các khối định dạng
  2. nó lặp lại các đoạn định dạng, kết xuất thành StringBuilder, về cơ bản là một mảng tự thay đổi kích thước khi cần thiết, bằng cách sao chép vào một mảng mới. điều này là cần thiết bởi vì chúng ta chưa biết phân bổ Chuỗi cuối cùng lớn đến mức nào
  3. StringBuilder.toString () sao chép bộ đệm nội bộ của mình thành một Chuỗi mới

nếu đích cuối cùng cho dữ liệu này là một luồng (ví dụ: hiển thị trang web hoặc ghi vào tệp), bạn có thể lắp ráp các đoạn định dạng trực tiếp vào luồng của mình:

new PrintStream(outputStream, autoFlush, encoding).format("hello {0}", "world");

Tôi suy đoán rằng trình tối ưu hóa sẽ tối ưu hóa việc xử lý chuỗi định dạng. Nếu vậy, bạn còn lại với hiệu suất được khấu hao tương đương để hủy thủ công String.format của bạn thành StringBuilder.


5
Tôi không nghĩ rằng suy đoán của bạn về tối ưu hóa xử lý chuỗi định dạng là chính xác. Trong một số thử nghiệm trong thế giới thực bằng Java 7, tôi thấy rằng việc sử dụng String.formatcác vòng lặp bên trong (chạy hàng triệu lần) đã dẫn đến hơn 10% thời gian thực hiện của tôi dành cho java.util.Formatter.parse(String). Điều này dường như chỉ ra rằng trong các vòng lặp bên trong, bạn nên tránh gọi Formatter.formathoặc bất cứ thứ gì gọi nó, bao gồm PrintStream.format(một lỗ hổng trong lib, IMO tiêu chuẩn của Java, đặc biệt là khi bạn không thể lưu trữ chuỗi định dạng được phân tích cú pháp).
Andy MacKinlay

8

Để mở rộng / sửa lỗi cho câu trả lời đầu tiên ở trên, thực tế không phải là String.format sẽ giúp với.
String.format sẽ giúp gì khi bạn in ngày / giờ (hoặc định dạng số, v.v.), trong đó có sự khác biệt về bản địa hóa (l10n) (tức là một số quốc gia sẽ in 04Feb2009 và các quốc gia khác sẽ in Feb042009).
Với dịch thuật, bạn chỉ nói về việc di chuyển bất kỳ chuỗi có thể gắn ngoài nào (như thông báo lỗi và không có gì) vào gói thuộc tính để bạn có thể sử dụng gói đúng cho ngôn ngữ phù hợp, sử dụng ResourceBundle và MessageFormat.

Nhìn vào tất cả những điều trên, tôi muốn nói rằng String.format so với kết nối đơn giản phù hợp với những gì bạn thích. Nếu bạn thích xem các cuộc gọi đến .format hơn nối, thì bằng mọi cách, hãy đi với điều đó.
Rốt cuộc, mã được đọc nhiều hơn nó được viết.


1
Tôi muốn nói rằng hiệu suất-khôn ngoan, String.format so với kết nối đơn giản xuất phát từ những gì bạn thích Tôi nghĩ rằng điều này là không chính xác. Hiệu suất-khôn ngoan, nối là tốt hơn nhiều. Để biết thêm chi tiết xin vui lòng xem câu trả lời của tôi.
Adam Stelmaszchot

6

Trong ví dụ của bạn, probalby hiệu suất không quá khác biệt nhưng có một số vấn đề khác cần xem xét: cụ thể là phân mảnh bộ nhớ. Ngay cả thao tác nối cũng đang tạo ra một chuỗi mới, ngay cả khi nó là tạm thời (cần có thời gian để GC và nó hoạt động nhiều hơn). String.format () chỉ dễ đọc hơn và nó liên quan đến việc phân mảnh ít hơn.

Ngoài ra, nếu bạn đang sử dụng một định dạng cụ thể rất nhiều, đừng quên bạn có thể sử dụng trực tiếp lớp Formatter () (tất cả String.format () sẽ khởi tạo một thể hiện Formatter một lần sử dụng).

Ngoài ra, một điều khác mà bạn cần lưu ý: hãy cẩn thận khi sử dụng chuỗi con (). Ví dụ:

String getSmallString() {
  String largeString = // load from file; say 2M in size
  return largeString.substring(100, 300);
}

Chuỗi lớn đó vẫn còn trong bộ nhớ vì đó chỉ là cách các chuỗi con Java hoạt động. Một phiên bản tốt hơn là:

  return new String(largeString.substring(100, 300));

hoặc là

  return String.format("%s", largeString.substring(100, 300));

Hình thức thứ hai có thể hữu ích hơn nếu bạn đang làm việc khác cùng một lúc.


8
Đáng để chỉ ra "câu hỏi liên quan" thực sự là C # và do đó không áp dụng được.
Không khí

bạn đã sử dụng công cụ nào để đo phân mảnh bộ nhớ và phân mảnh thậm chí có tạo ra sự khác biệt về tốc độ cho ram không?
kritzikratzi

Cần chỉ ra rằng phương thức chuỗi con đã được thay đổi từ Java 7 +. Bây giờ nó sẽ trả về một đại diện Chuỗi mới chỉ chứa các ký tự được chuỗi con. Điều đó có nghĩa là không cần phải trả lại chuỗi cuộc gọi :: mới
João Rebelo

5

Nói chung, bạn nên sử dụng String.Format vì nó tương đối nhanh và nó hỗ trợ toàn cầu hóa (giả sử bạn thực sự đang cố gắng viết một cái gì đó được đọc bởi người dùng). Nó cũng giúp toàn cầu hóa dễ dàng hơn nếu bạn đang cố dịch một chuỗi so với 3 hoặc nhiều hơn cho mỗi câu (đặc biệt là đối với các ngôn ngữ có cấu trúc ngữ pháp khác nhau đáng kể).

Bây giờ nếu bạn không bao giờ có kế hoạch dịch bất cứ thứ gì, thì hãy dựa vào chuyển đổi + toán tử được tích hợp sẵn của Java thành StringBuilder. Hoặc sử dụng Java StringBuildermột cách rõ ràng.


3

Một góc nhìn khác từ Chỉ quan điểm đăng nhập.

Tôi thấy rất nhiều cuộc thảo luận liên quan đến việc đăng nhập vào chủ đề này vì vậy đã nghĩ đến việc thêm kinh nghiệm của tôi vào câu trả lời. Có thể ai đó sẽ thấy nó hữu ích.

Tôi đoán động cơ của việc đăng nhập bằng cách sử dụng trình định dạng xuất phát từ việc tránh nối chuỗi. Về cơ bản, bạn không muốn có một tổng phí chuỗi concat nếu bạn không đăng nhập nó.

Bạn không thực sự cần concat / format trừ khi bạn muốn đăng nhập. Hãy nói nếu tôi định nghĩa một phương thức như thế này

public void logDebug(String... args, Throwable t) {
    if(debugOn) {
       // call concat methods for all args
       //log the final debug message
    }
}

Theo cách tiếp cận này, cancat / formatter hoàn toàn không được gọi nếu thông báo gỡ lỗi và debugOn = false

Mặc dù vậy vẫn sẽ tốt hơn khi sử dụng StringBuilder thay vì định dạng ở đây. Động lực chính là để tránh bất kỳ điều đó.

Đồng thời tôi không thích thêm khối "nếu" cho mỗi câu lệnh ghi nhật ký kể từ khi

  • Nó ảnh hưởng đến khả năng đọc
  • Giảm phạm vi bảo hiểm trong các bài kiểm tra đơn vị của tôi - đó là điều khó hiểu khi bạn muốn đảm bảo mọi dòng đều được kiểm tra.

Do đó, tôi thích tạo một lớp tiện ích ghi nhật ký với các phương thức như trên và sử dụng nó ở mọi nơi mà không phải lo lắng về hiệu năng và bất kỳ vấn đề nào khác liên quan đến nó.


Bạn có thể tận dụng một thư viện hiện có như slf4j-api để giải quyết vấn đề này với tính năng ghi nhật ký tham số của họ không? slf4j.org/faq.html#logging_performance
ammianus

2

Tôi vừa sửa đổi thử nghiệm của hhafez để bao gồm StringBuilder. StringBuilder nhanh hơn 33 lần so với String.format khi sử dụng máy khách jdk 1.6.0_10 trên XP. Sử dụng chuyển đổi -server làm giảm hệ số xuống 20.

public class StringTest {

   public static void main( String[] args ) {
      test();
      test();
   }

   private static void test() {
      int i = 0;
      long prev_time = System.currentTimeMillis();
      long time;

      for ( i = 0; i < 1000000; i++ ) {
         String s = "Blah" + i + "Blah";
      }
      time = System.currentTimeMillis() - prev_time;

      System.out.println("Time after for loop " + time);

      prev_time = System.currentTimeMillis();
      for ( i = 0; i < 1000000; i++ ) {
         String s = String.format("Blah %d Blah", i);
      }
      time = System.currentTimeMillis() - prev_time;
      System.out.println("Time after for loop " + time);

      prev_time = System.currentTimeMillis();
      for ( i = 0; i < 1000000; i++ ) {
         new StringBuilder("Blah").append(i).append("Blah");
      }
      time = System.currentTimeMillis() - prev_time;
      System.out.println("Time after for loop " + time);
   }
}

Mặc dù điều này nghe có vẻ quyết liệt, tôi cho rằng nó chỉ có liên quan trong một số trường hợp hiếm hoi, bởi vì các số tuyệt đối khá thấp: 4 giây cho 1 triệu cuộc gọi String.format đơn giản là ổn - miễn là tôi sử dụng chúng để đăng nhập hoặc giống.

Cập nhật: Như được chỉ ra bởi sjbotha trong các bình luận, bài kiểm tra StringBuilder không hợp lệ, vì nó thiếu một trận chung kết .toString().

Hệ số tăng tốc chính xác từ String.format(.)đến StringBuilderlà 23 trên máy của tôi (16 với công -servertắc).


1
Bài kiểm tra của bạn không hợp lệ vì không tính đến thời gian ăn hết chỉ bằng một vòng lặp. Bạn nên bao gồm điều đó và trừ nó khỏi tất cả các kết quả khác, ở mức tối thiểu (vâng, nó có thể là một tỷ lệ đáng kể).
cletus

Tôi đã làm điều đó, vòng lặp for mất 0 ms. Nhưng ngay cả khi nó mất thời gian, điều này sẽ chỉ làm tăng yếu tố.
the.duckman

3
Kiểm tra StringBuilder không hợp lệ vì cuối cùng nó không gọi toString () để thực sự cung cấp cho bạn một Chuỗi bạn có thể sử dụng. Tôi đã thêm điều này và kết quả là StringBuilder mất khoảng thời gian tương đương với +. Tôi chắc rằng khi bạn tăng số lượng các phụ lục thì cuối cùng nó sẽ trở nên rẻ hơn.
Sarel Botha

1

Đây là phiên bản sửa đổi của mục hhafez. Nó bao gồm một tùy chọn xây dựng chuỗi.

public class BLA
{
public static final String BLAH = "Blah ";
public static final String BLAH2 = " Blah";
public static final String BLAH3 = "Blah %d Blah";


public static void main(String[] args) {
    int i = 0;
    long prev_time = System.currentTimeMillis();
    long time;
    int numLoops = 1000000;

    for( i = 0; i< numLoops; i++){
        String s = BLAH + i + BLAH2;
    }
    time = System.currentTimeMillis() - prev_time;

    System.out.println("Time after for loop " + time);

    prev_time = System.currentTimeMillis();
    for( i = 0; i<numLoops; i++){
        String s = String.format(BLAH3, i);
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

    prev_time = System.currentTimeMillis();
    for( i = 0; i<numLoops; i++){
        StringBuilder sb = new StringBuilder();
        sb.append(BLAH);
        sb.append(i);
        sb.append(BLAH2);
        String s = sb.toString();
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

}

}

Thời gian sau vòng lặp 391 Thời gian sau vòng lặp 4163 Thời gian sau vòng lặp 227


0

Câu trả lời cho điều này phụ thuộc rất nhiều vào cách trình biên dịch Java cụ thể của bạn tối ưu hóa mã byte mà nó tạo ra. Các chuỗi là bất biến và theo lý thuyết, mỗi hoạt động "+" có thể tạo ra một chuỗi mới. Nhưng, trình biên dịch của bạn gần như chắc chắn tối ưu hóa các bước tạm thời trong việc xây dựng chuỗi dài. Hoàn toàn có thể là cả hai dòng mã ở trên tạo ra cùng một mã byte.

Cách thực sự duy nhất để biết là kiểm tra mã lặp trong môi trường hiện tại của bạn. Viết một ứng dụng QĐ nối chuỗi cả hai cách lặp và xem cách chúng hết thời gian với nhau.


1
Mã byte cho ví dụ thứ hai chắc chắn gọi String.format, nhưng tôi sẽ kinh hoàng nếu một phép nối đơn giản đã làm. Tại sao trình biên dịch sẽ sử dụng một chuỗi định dạng mà sau đó sẽ phải được phân tích cú pháp?
Jon Skeet

Tôi đã sử dụng "mã byte" trong đó đáng lẽ tôi phải nói "mã nhị phân". Khi tất cả đi xuống jmps và Mov, nó cũng có thể là cùng một mã.
Vâng - Jake đó.

0

Xem xét sử dụng "hello".concat( "world!" )cho số lượng nhỏ các chuỗi trong nối. Nó có thể thậm chí tốt hơn cho hiệu suất hơn so với các phương pháp khác.

Nếu bạn có nhiều hơn 3 chuỗi, hãy xem xét sử dụng StringBuilder hoặc chỉ Chuỗi, tùy thuộc vào trình biên dịch mà bạn sử dụng.

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.