Sử dụng lại StringBuilder trong một vòng lặp có tốt hơn không?


101

Tôi có một câu hỏi liên quan đến hiệu suất liên quan đến việc sử dụng StringBuilder. Trong một vòng lặp rất dài, tôi đang thao tác a StringBuildervà chuyển nó sang một phương thức khác như sau:

for (loop condition) {
    StringBuilder sb = new StringBuilder();
    sb.append("some string");
    . . .
    sb.append(anotherString);
    . . .
    passToMethod(sb.toString());
}

Khởi tạo StringBuilderở mọi chu kỳ vòng lặp có phải là một giải pháp tốt không? Và thay vào đó, gọi xóa có tốt hơn không, như sau?

StringBuilder sb = new StringBuilder();
for (loop condition) {
    sb.delete(0, sb.length);
    sb.append("some string");
    . . .
    sb.append(anotherString);
    . . .
    passToMethod(sb.toString());
}

Câu trả lời:


69

Cái thứ hai nhanh hơn khoảng 25% trong điểm chuẩn nhỏ của tôi.

public class ScratchPad {

    static String a;

    public static void main( String[] args ) throws Exception {
        long time = System.currentTimeMillis();
        for( int i = 0; i < 10000000; i++ ) {
            StringBuilder sb = new StringBuilder();
            sb.append( "someString" );
            sb.append( "someString2"+i );
            sb.append( "someStrin4g"+i );
            sb.append( "someStr5ing"+i );
            sb.append( "someSt7ring"+i );
            a = sb.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
        time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for( int i = 0; i < 10000000; i++ ) {
            sb.delete( 0, sb.length() );
            sb.append( "someString" );
            sb.append( "someString2"+i );
            sb.append( "someStrin4g"+i );
            sb.append( "someStr5ing"+i );
            sb.append( "someSt7ring"+i );
            a = sb.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
    }
}

Các kết quả:

25265
17969

Lưu ý rằng điều này là với JRE 1.6.0_07.


Dựa trên ý tưởng của Jon Skeet trong bản chỉnh sửa, đây là phiên bản 2. Tuy nhiên, kết quả tương tự.

public class ScratchPad {

    static String a;

    public static void main( String[] args ) throws Exception {
        long time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for( int i = 0; i < 10000000; i++ ) {
            sb.delete( 0, sb.length() );
            sb.append( "someString" );
            sb.append( "someString2" );
            sb.append( "someStrin4g" );
            sb.append( "someStr5ing" );
            sb.append( "someSt7ring" );
            a = sb.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
        time = System.currentTimeMillis();
        for( int i = 0; i < 10000000; i++ ) {
            StringBuilder sb2 = new StringBuilder();
            sb2.append( "someString" );
            sb2.append( "someString2" );
            sb2.append( "someStrin4g" );
            sb2.append( "someStr5ing" );
            sb2.append( "someSt7ring" );
            a = sb2.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
    }
}

Các kết quả:

5016
7516

4
Tôi đã thêm một chỉnh sửa trong câu trả lời của mình để giải thích tại sao điều này có thể xảy ra. Tôi sẽ xem xét cẩn thận hơn sau (45 phút). Lưu ý rằng làm nối trong các cuộc gọi append giảm điểm của việc sử dụng StringBuilder ở nơi đầu tiên phần nào :)
Jon Skeet

3
Ngoài ra, sẽ rất thú vị để xem điều gì xảy ra nếu bạn đảo ngược hai khối - JIT vẫn đang "hâm nóng" StringBuilder trong lần thử nghiệm đầu tiên. Nó có thể không liên quan, nhưng thú vị để thử.
Jon Skeet

1
Tôi vẫn muốn sử dụng phiên bản đầu tiên vì nó sạch hơn . Nhưng thật tốt là bạn đã thực sự thực hiện được điểm chuẩn :) Thay đổi được đề xuất tiếp theo: hãy thử # 1 với công suất thích hợp được truyền vào hàm tạo.
Jon Skeet

25
Sử dụng sb.setLength (0); thay vào đó, đó là cách nhanh nhất để làm trống nội dung của StringBuilder chống lại việc tạo lại đối tượng hoặc sử dụng .delete (). Lưu ý rằng điều này không áp dụng cho StringBuffer, các kiểm tra đồng thời của nó sẽ vô hiệu hóa lợi thế về tốc độ.
P Arrayah

1
Câu trả lời không hiệu quả. P Arrayah và Dave Jarvis đúng. setLength (0) là câu trả lời hiệu quả nhất. StringBuilder được hỗ trợ bởi một mảng char và có thể thay đổi. Tại điểm .toString () được gọi, mảng char được sao chép và được sử dụng để sao lưu một chuỗi bất biến. Tại thời điểm này, bộ đệm có thể thay đổi của StringBuilder có thể được sử dụng lại, đơn giản bằng cách di chuyển con trỏ chèn trở về 0 (thông qua .setLength (0)). sb.toString tạo thêm một bản sao khác (mảng char bất biến), vì vậy mỗi lần lặp yêu cầu hai bộ đệm trái ngược với phương thức .setLength (0) chỉ yêu cầu một bộ đệm mới cho mỗi vòng lặp.
Chris

25

Theo triết lý của việc viết mã solid, tốt hơn hết là bạn nên đặt StringBuilder vào bên trong vòng lặp của bạn. Bằng cách này, nó không đi ra ngoài mã dự định của nó.

Thứ hai, ứng biến lớn nhất trong StringBuilder đến từ việc tạo cho nó một kích thước ban đầu để tránh nó lớn hơn trong khi vòng lặp chạy

for (loop condition) {
  StringBuilder sb = new StringBuilder(4096);
}

1
Bạn luôn có thể xác định phạm vi toàn bộ bằng các dấu ngoặc nhọn, theo cách đó bạn không có Stringbuilder bên ngoài.
Epaga

@Epaga: Nó vẫn nằm ngoài vòng lặp. Có, nó không gây ô nhiễm phạm vi bên ngoài, nhưng đó là một cách không tự nhiên để viết mã để cải thiện hiệu suất mà chưa được xác minh trong ngữ cảnh .
Jon Skeet

Hoặc thậm chí tốt hơn, đặt toàn bộ điều theo phương pháp riêng của nó. ;-) Nhưng tôi nghe thấy ya re: context.
Epaga

Tốt hơn hết hãy khởi tạo với kích thước dự kiến ​​thay vì tổng số tùy ý (4096) Mã của bạn có thể trả về một Chuỗi tham chiếu đến một char [] có kích thước 4096 (phụ thuộc vào JDK; theo tôi nhớ thì đó là trường hợp của 1.4)
kohlerm

24

Vẫn còn nhanh hơn:

public class ScratchPad {

    private static String a;

    public static void main( String[] args ) throws Exception {
        long time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder( 128 );

        for( int i = 0; i < 10000000; i++ ) {
            // Resetting the string is faster than creating a new object.
            // Since this is a critical loop, every instruction counts.
            //
            sb.setLength( 0 );
            sb.append( "someString" );
            sb.append( "someString2" );
            sb.append( "someStrin4g" );
            sb.append( "someStr5ing" );
            sb.append( "someSt7ring" );
            setA( sb.toString() );
        }

        System.out.println( System.currentTimeMillis()-time );
    }

    private static void setA( String aString ) {
        a = aString;
    }
}

Trong triết lý viết mã solid, hoạt động bên trong của phương thức nên được ẩn khỏi các đối tượng sử dụng phương thức. Vì vậy, nó không có gì khác biệt so với quan điểm của hệ thống cho dù bạn khai báo lại StringBuilder trong vòng lặp hay bên ngoài vòng lặp. Vì khai báo nó bên ngoài vòng lặp nhanh hơn và nó không làm cho mã phức tạp hơn để đọc, nên hãy sử dụng lại đối tượng thay vì khôi phục nó.

Ngay cả khi mã phức tạp hơn và bạn biết chắc chắn rằng việc tạo đối tượng là điểm nghẽn, hãy bình luận nó.

Ba lượt chạy với câu trả lời này:

$ java ScratchPad
1567
$ java ScratchPad
1569
$ java ScratchPad
1570

Ba lượt chạy với câu trả lời còn lại:

$ java ScratchPad2
1663
2231
$ java ScratchPad2
1656
2233
$ java ScratchPad2
1658
2242

Mặc dù không đáng kể, việc thiết lập StringBuilderkích thước bộ đệm ban đầu của nó sẽ mang lại một lợi ích nhỏ.


3
Đây là câu trả lời tốt nhất cho đến nay. StringBuilder được hỗ trợ bởi một mảng char và có thể thay đổi. Tại điểm .toString () được gọi, mảng char được sao chép và được sử dụng để sao lưu một chuỗi bất biến. Tại thời điểm này, bộ đệm có thể thay đổi của StringBuilder có thể được sử dụng lại, đơn giản bằng cách di chuyển con trỏ chèn trở về 0 (thông qua .setLength (0)). Những câu trả lời gợi ý phân bổ một StringBuilder hoàn toàn mới cho mỗi vòng lặp dường như không nhận ra rằng .toString tạo ra một bản sao khác, vì vậy mỗi lần lặp yêu cầu hai bộ đệm trái ngược với phương thức .setLength (0) chỉ yêu cầu một bộ đệm mới trên mỗi vòng lặp.
Chris

12

Được rồi, bây giờ tôi hiểu chuyện gì đang xảy ra và điều đó có ý nghĩa.

Tôi có ấn tượng rằng toStringchỉ cần chuyển phần bên dưới char[]vào một phương thức khởi tạo chuỗi mà không có bản sao. Sau đó, một bản sao sẽ được thực hiện trên thao tác "ghi" tiếp theo (ví dụ delete). Tôi tin rằng đây trường hợp của StringBuffermột số phiên bản trước. (Nó không phải bây giờ.) Nhưng không - toStringchỉ cần chuyển mảng (và chỉ số và độ dài) cho hàm tạo công cộng Stringđể lấy một bản sao.

Vì vậy, trong StringBuildertrường hợp "tái sử dụng ", chúng tôi thực sự tạo một bản sao dữ liệu trên mỗi chuỗi, sử dụng cùng một mảng char trong bộ đệm trong toàn bộ thời gian. Rõ ràng việc tạo mới StringBuildermỗi lần sẽ tạo ra một bộ đệm cơ bản mới - và sau đó bộ đệm đó được sao chép (hơi vô nghĩa, trong trường hợp cụ thể của chúng tôi, nhưng được thực hiện vì lý do an toàn) khi tạo một chuỗi mới.

Tất cả điều này dẫn đến phiên bản thứ hai chắc chắn hiệu quả hơn - nhưng đồng thời tôi vẫn nói rằng đó là mã xấu hơn.


Chỉ là một số thông tin hài hước về .NET, có một tình huống khác. .NET StringBuilder sửa đổi nội bộ đối tượng "string" thông thường và phương thức toString chỉ cần trả về nó (đánh dấu nó là không thể sửa đổi, do đó, các thao tác StringBuilder sẽ tạo lại nó). Vì vậy, chuỗi "StringBuilder mới-> sửa đổi nó-> thành Chuỗi" điển hình sẽ không tạo thêm bất kỳ bản sao nào (chỉ để mở rộng bộ nhớ hoặc thu nhỏ nó, nếu độ dài chuỗi kết quả ngắn hơn nhiều so với dung lượng của nó). Trong Java chu trình này luôn tạo ít nhất một bản sao (trong StringBuilder.toString ()).
Ivan Dubrov,

Sun JDK trước 1.5 có sự tối ưu hóa mà bạn đang giả định: bug.sun.com/bugdatabase/view_bug.do?bug_id=6219959
Dan Berindei

9

Vì tôi chưa nghĩ rằng nó đã được chỉ ra, vì các tối ưu hóa được tích hợp trong trình biên dịch Sun Java, trình biên dịch này tự động tạo StringBuilders (StringBuffers trước J2SE 5.0) khi nó nhìn thấy các nối chuỗi, ví dụ đầu tiên trong câu hỏi tương đương với:

for (loop condition) {
  String s = "some string";
  . . .
  s += anotherString;
  . . .
  passToMethod(s);
}

Cái nào dễ đọc hơn, IMO, cách tiếp cận tốt hơn. Những nỗ lực của bạn để tối ưu hóa có thể dẫn đến lợi ích trong một số nền tảng, nhưng có khả năng làm mất các nền tảng khác.

Nhưng nếu bạn thực sự đang gặp vấn đề với hiệu suất, thì chắc chắn, hãy tối ưu hóa đi. Tuy nhiên, tôi sẽ bắt đầu với việc chỉ định rõ ràng kích thước bộ đệm của StringBuilder, theo Jon Skeet.


4

JVM hiện đại thực sự thông minh về những thứ như thế này. Tôi sẽ không đoán nó lần thứ hai và làm một cái gì đó hacky ít có thể bảo trì / đọc được ... trừ khi bạn thực hiện các điểm chuẩn thích hợp với dữ liệu sản xuất xác nhận một cải tiến hiệu suất không tầm thường (và ghi lại nó;)


Trong đó "không tầm thường" là chìa khóa - điểm chuẩn có thể hiển thị một biểu mẫu nhanh hơn tương ứng , nhưng không có gợi ý về lượng thời gian đang sử dụng trong ứng dụng thực :)
Jon Skeet

Xem điểm chuẩn trong câu trả lời của tôi bên dưới. Cách thứ hai nhanh hơn.
Epaga

1
@Epaga: Điểm chuẩn của bạn cho biết rất ít về sự cải thiện hiệu suất trong ứng dụng thực, trong đó thời gian thực hiện phân bổ StringBuilder có thể nhỏ so với phần còn lại của vòng lặp. Đó là lý do tại sao ngữ cảnh lại quan trọng trong việc đo điểm chuẩn.
Jon Skeet

1
@Epaga: Cho đến khi anh ấy đo nó bằng mã thật của mình, chúng tôi sẽ không biết nó thực sự quan trọng như thế nào. Nếu có nhiều mã cho mỗi lần lặp lại của vòng lặp, tôi thực sự nghi ngờ rằng nó vẫn không liên quan. Chúng tôi không biết có gì trong "..."
Jon Skeet

1
(Đừng hiểu sai ý tôi, btw - bản thân kết quả điểm chuẩn của bạn vẫn rất thú vị. Tôi bị thu hút bởi microbenchmarks. Tôi chỉ không thích bẻ cong mã của mình trước khi thực hiện các bài kiểm tra thực tế.)
Jon Skeet

4

Dựa trên kinh nghiệm của tôi với việc phát triển phần mềm trên Windows, tôi sẽ nói rằng việc xóa StringBuilder ra trong vòng lặp của bạn có hiệu suất tốt hơn so với việc tạo StringBuilder với mỗi lần lặp lại. Xóa nó sẽ giải phóng bộ nhớ bị ghi đè ngay lập tức mà không cần cấp phát bổ sung. Tôi không đủ quen thuộc với trình thu gom rác Java, nhưng tôi nghĩ rằng việc giải phóng và không phân bổ lại (trừ khi chuỗi tiếp theo của bạn phát triển StringBuilder) có lợi hơn so với việc khởi tạo.

(Ý kiến ​​của tôi trái ngược với những gì mọi người khác đang đề xuất. Hừm. Đã đến lúc làm điểm chuẩn cho nó.)


Vấn đề là dù sao thì nhiều bộ nhớ hơn cũng phải được phân bổ lại, vì dữ liệu hiện có đang được sử dụng bởi Chuỗi mới được tạo ở cuối lần lặp vòng lặp trước.
Jon Skeet

Ồ, điều đó có ý nghĩa, tôi đã mặc dù rằng toString đang cấp phát và trả về một cá thể chuỗi mới và bộ đệm byte cho trình tạo đang xóa thay vì phân bổ lại.
cfeduke 28/10/08

Điểm chuẩn của Epaga cho thấy rằng việc xóa và sử dụng lại là một lợi ích so với việc khởi tạo ở mỗi lần vượt qua.
cfeduke 28/10/08

1

Lý do tại sao thực hiện 'setLength' hoặc 'delete' cải thiện hiệu suất chủ yếu là mã 'học' kích thước phù hợp của bộ đệm và ít thực hiện cấp phát bộ nhớ hơn. Nói chung, tôi khuyên bạn nên để trình biên dịch thực hiện tối ưu hóa chuỗi . Tuy nhiên, nếu hiệu suất là quan trọng, tôi thường tính toán trước kích thước mong đợi của bộ đệm. Kích thước StringBuilder mặc định là 16 ký tự. Nếu bạn phát triển vượt quá mức đó, thì nó phải thay đổi kích thước. Thay đổi kích thước là nơi hiệu suất bị mất. Đây là một điểm chuẩn nhỏ khác minh họa điều này:

private void clear() throws Exception {
    long time = System.currentTimeMillis();
    int maxLength = 0;
    StringBuilder sb = new StringBuilder();

    for( int i = 0; i < 10000000; i++ ) {
        // Resetting the string is faster than creating a new object.
        // Since this is a critical loop, every instruction counts.
        //
        sb.setLength( 0 );
        sb.append( "someString" );
        sb.append( "someString2" ).append( i );
        sb.append( "someStrin4g" ).append( i );
        sb.append( "someStr5ing" ).append( i );
        sb.append( "someSt7ring" ).append( i );
        maxLength = Math.max(maxLength, sb.toString().length());
    }

    System.out.println(maxLength);
    System.out.println("Clear buffer: " + (System.currentTimeMillis()-time) );
}

private void preAllocate() throws Exception {
    long time = System.currentTimeMillis();
    int maxLength = 0;

    for( int i = 0; i < 10000000; i++ ) {
        StringBuilder sb = new StringBuilder(82);
        sb.append( "someString" );
        sb.append( "someString2" ).append( i );
        sb.append( "someStrin4g" ).append( i );
        sb.append( "someStr5ing" ).append( i );
        sb.append( "someSt7ring" ).append( i );
        maxLength = Math.max(maxLength, sb.toString().length());
    }

    System.out.println(maxLength);
    System.out.println("Pre allocate: " + (System.currentTimeMillis()-time) );
}

public void testBoth() throws Exception {
    for(int i = 0; i < 5; i++) {
        clear();
        preAllocate();
    }
}

Kết quả cho thấy việc tái sử dụng đối tượng nhanh hơn khoảng 10% so với việc tạo bộ đệm có kích thước như mong đợi.


1

LOL - Liên minh huyền thoại, lần đầu tiên tôi thấy mọi người so sánh hiệu suất bằng cách kết hợp chuỗi trong StringBuilder. Vì mục đích đó, nếu bạn sử dụng "+", nó có thể nhanh hơn; D. Mục đích của việc sử dụng StringBuilder để tăng tốc độ truy xuất toàn bộ chuỗi như khái niệm "địa phương".

Trong trường hợp bạn truy xuất một giá trị Chuỗi thường xuyên mà không cần thay đổi thường xuyên, Stringbuilder cho phép hiệu suất truy xuất chuỗi cao hơn. Và đó là mục đích của việc sử dụng Stringbuilder .. vui lòng không MIS-Kiểm tra mục đích cốt lõi của việc đó ..

Một số người nói, Máy bay bay nhanh hơn. Do đó, tôi thử nghiệm nó với chiếc xe đạp của mình và nhận thấy rằng máy bay di chuyển chậm hơn. Bạn có biết cách tôi đặt cài đặt thử nghiệm không; D


1

Không nhanh hơn đáng kể, nhưng từ các thử nghiệm của tôi, nó cho thấy trung bình nhanh hơn vài mili giây khi sử dụng 1.6.0_45 64 bit: sử dụng StringBuilder.setLength (0) thay vì StringBuilder.delete ():

time = System.currentTimeMillis();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < 10000000; i++) {
    sb2.append( "someString" );
    sb2.append( "someString2"+i );
    sb2.append( "someStrin4g"+i );
    sb2.append( "someStr5ing"+i );
    sb2.append( "someSt7ring"+i );
    a = sb2.toString();
    sb2.setLength(0);
}
System.out.println( System.currentTimeMillis()-time );

1

Cách nhanh nhất là sử dụng "setLength". Nó sẽ không liên quan đến hoạt động sao chép. Cách tạo một StringBuilder mới sẽ hoàn toàn không có . StringBuilder.delete (int start, int end) chậm là do nó sẽ sao chép lại mảng cho phần thay đổi kích thước.

 System.arraycopy(value, start+len, value, start, count-end);

Sau đó, StringBuilder.delete () sẽ cập nhật StringBuilder.count lên kích thước mới. Trong khi StringBuilder.setLength () chỉ đơn giản hóa việc cập nhật StringBuilder.count lên kích thước mới.


0

Đầu tiên là tốt hơn cho con người. Nếu thứ hai nhanh hơn một chút trên một số phiên bản của một số JVM, thì sao?

Nếu hiệu suất là quan trọng, hãy bỏ qua StringBuilder và viết của riêng bạn. Nếu bạn là một lập trình viên giỏi và tính đến cách ứng dụng của bạn đang sử dụng chức năng này, bạn sẽ có thể làm cho nó nhanh hơn nữa. Đáng giá? Chắc là không.

Tại sao câu hỏi này lại được coi là "câu hỏi yêu thích"? Bởi vì tối ưu hóa hiệu suất rất thú vị, bất kể nó có thực tế hay không.


Nó không chỉ là một câu hỏi học thuật. Trong khi hầu hết các lần (đọc 95%) Tôi thích khả năng đọc và khả năng bảo trì, có thực sự những trường hợp mà cải thiện chút làm cho sự khác biệt lớn ...
Pier Luigi

OK, tôi sẽ thay đổi câu trả lời của mình. Nếu một đối tượng cung cấp một phương thức cho phép nó được xóa và sử dụng lại, thì hãy làm như vậy. Kiểm tra mã trước nếu bạn muốn đảm bảo mã rõ ràng là hiệu quả; có thể nó phát hành một mảng riêng tư! Nếu hiệu quả, hãy cấp phát đối tượng bên ngoài vòng lặp và sử dụng lại bên trong.
dongilmore

0

Tôi không nghĩ rằng nên cố gắng tối ưu hóa hiệu suất như vậy. Hôm nay (2019) cả hai trạng thái đang chạy khoảng 11 giây cho 100.000.000 vòng lặp trên Máy tính xách tay I5 của tôi:

    String a;
    StringBuilder sb = new StringBuilder();
    long time = 0;

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuilder sb3 = new StringBuilder();
        sb3.append("someString");
        sb3.append("someString2");
        sb3.append("someStrin4g");
        sb3.append("someStr5ing");
        sb3.append("someSt7ring");
        a = sb3.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        sb.setLength(0);
        sb.delete(0, sb.length());
        sb.append("someString");
        sb.append("someString2");
        sb.append("someStrin4g");
        sb.append("someStr5ing");
        sb.append("someSt7ring");
        a = sb.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

==> 11000 msec (khai báo bên trong vòng lặp) và 8236 msec (khai báo bên ngoài vòng lặp)

Ngay cả khi tôi đang chạy các chương trình để công khai địa chỉ với một số tỷ vòng lặp, chênh lệch 2 giây. đối với 100 triệu vòng lặp không tạo ra bất kỳ sự khác biệt nào bởi vì các chương trình đó đang chạy trong nhiều giờ. Cũng xin lưu ý rằng mọi thứ sẽ khác nếu bạn chỉ có một câu lệnh phụ:

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuilder sb3 = new StringBuilder();
        sb3.append("someString");
            a = sb3.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        sb.setLength(0);
        sb.delete(0, sb.length());
        sb.append("someString");
        a = sb.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

==> 3416 msec (vòng lặp bên trong), 3555 msec (vòng lặp bên ngoài) Câu lệnh đầu tiên tạo StringBuilder trong vòng lặp nhanh hơn trong trường hợp đó. Và, nếu bạn thay đổi thứ tự thực hiện thì nhanh hơn nhiều:

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        sb.setLength(0);
        sb.delete(0, sb.length());
        sb.append("someString");
        a = sb.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuilder sb3 = new StringBuilder();
        sb3.append("someString");
            a = sb3.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

==> 3638 msec (vòng lặp bên ngoài), 2908 msec (vòng lặp bên trong)

Trân trọng, Ulrich


-2

Khai báo một lần và chỉ định mỗi lần. Đó là một khái niệm thực dụng hơn và có thể tái sử dụng hơn là một sự tối ưu hóa.

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.