it-swarm-vi.tech

Chuỗi so với StringBuilder

Tôi hiểu sự khác biệt giữa StringStringBuilder (StringBuilder là có thể thay đổi) nhưng có sự khác biệt lớn về hiệu suất giữa hai loại không?

Chương trình mà tôi làm việc trên có rất nhiều trường hợp nối chuỗi điều khiển (500+). Việc sử dụng StringBuilder có phải là sự lựa chọn tốt hơn không?

202
Kuvo

Có, sự khác biệt hiệu suất là đáng kể. Xem bài viết KB " Cách cải thiện hiệu suất nối chuỗi trong Visual C # ".

Tôi đã luôn cố gắng viết mã cho rõ ràng trước, và sau đó tối ưu hóa cho hiệu suất sau này. Điều đó dễ hơn nhiều so với cách khác! Tuy nhiên, đã thấy sự khác biệt hiệu năng rất lớn trong các ứng dụng của tôi giữa hai ứng dụng, bây giờ tôi nghĩ về nó cẩn thận hơn một chút.

May mắn thay, việc chạy phân tích hiệu suất trên mã của bạn tương đối đơn giản để xem bạn đang dành thời gian ở đâu và sau đó sửa đổi nó để sử dụng StringBuilder khi cần.

224
Jay Bazuzi

Để làm rõ những gì Chung Hân Đồng nói về 4 chuỗi, nếu bạn có một cái gì đó như thế này:

string a,b,c,d;
 a = b + c + d;

sau đó nó sẽ nhanh hơn khi sử dụng chuỗi và toán tử cộng. Điều này là do (như Java, như Eric chỉ ra), bên trong nó sử dụng StringBuilder tự động (Trên thực tế, nó sử dụng một nguyên thủy mà StringBuilder cũng sử dụng)

Tuy nhiên, nếu những gì bạn đang làm gần hơn với:

string a,b,c,d;
 a = a + b;
 a = a + c;
 a = a + d;

Sau đó, bạn cần sử dụng StringBuilder một cách rõ ràng. .Net không tự động tạo StringBuilder ở đây, vì nó sẽ là vô nghĩa. Ở cuối mỗi dòng, "a" phải là một chuỗi (không thay đổi), do đó, nó sẽ phải tạo và loại bỏ StringBuilder trên mỗi dòng. Để tăng tốc, bạn cần sử dụng cùng một StringBuilder cho đến khi bạn hoàn thành việc xây dựng:

string a,b,c,d;
StringBuilder e = new StringBuilder();
 e.Append(b);
 e.Append(c);
 e.Append(d);
 a = e.ToString();
54
James Curran

StringBuilder thích hợp hơn IF bạn đang thực hiện nhiều vòng lặp hoặc các nhánh trong mã của bạn vượt qua ... tuy nhiên, đối với hiệu suất PURE, nếu bạn có thể thoát khỏi bằng SINGLE = khai báo chuỗi, thì đó là hiệu suất cao hơn nhiều.

Ví dụ:

string myString = "Some stuff" + var1 + " more stuff"
                  + var2 + " other stuff" .... etc... etc...;

hiệu quả hơn

StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..

Trong trường hợp này, StringBuild có thể được coi là dễ bảo trì hơn, nhưng không hiệu quả hơn so với khai báo chuỗi đơn.

9 trong số 10 lần mặc dù ... sử dụng trình tạo chuỗi.

Một lưu ý phụ: chuỗi + var cũng hiệu quả hơn khi tiếp cận chuỗi.Format (nói chung) sử dụng StringBuilder bên trong (khi nghi ngờ ... hãy kiểm tra bộ phản xạ!)

28
calebjenkins

Điểm chuẩn này cho thấy việc nối thường xuyên sẽ nhanh hơn khi kết hợp 3 chuỗi hoặc ít hơn.

http://www.chinhdo.com/20070224/opesbuilder-is-not-always-faster/

StringBuilder có thể cải thiện đáng kể việc sử dụng bộ nhớ, đặc biệt là trong trường hợp bạn thêm 500 chuỗi với nhau.

Hãy xem xét ví dụ sau:

string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
    buffer += i.ToString();
}
return buffer;

Điều gì xảy ra trong bộ nhớ? Các chuỗi sau đây được tạo:

1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"

Bằng cách thêm năm số đó vào cuối chuỗi, chúng tôi đã tạo ra 13 đối tượng chuỗi! Và 12 người trong số họ là vô dụng! Ồ

StringBuilder khắc phục vấn đề này. Nó không phải là "chuỗi có thể thay đổi" như chúng ta thường nghe ( tất cả các chuỗi trong .NET là bất biến ). Nó hoạt động bằng cách giữ một bộ đệm nội bộ, một mảng char. Gọi Append () hoặc AppendLine () thêm chuỗi vào khoảng trống ở cuối mảng char; nếu mảng quá nhỏ, nó tạo ra một mảng mới, lớn hơn và sao chép bộ đệm ở đó. Vì vậy, trong ví dụ trên, StringBuilder có thể chỉ cần một mảng duy nhất để chứa tất cả 5 bổ sung cho chuỗi - tùy thuộc vào kích thước bộ đệm của nó. Bạn có thể cho StringBuilder biết bộ đệm của nó sẽ lớn như thế nào trong hàm tạo.

23
Matt Trunnell

Một ví dụ đơn giản để chứng minh sự khác biệt về tốc độ khi sử dụng nối String so với StringBuilder:

System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
    test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");

Kết quả:

Sử dụng nối chuỗi: 15423 mili giây

StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
    test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");

Kết quả:

Sử dụng StringBuilder: 10 mili giây

Kết quả là, lần lặp đầu tiên mất 15423 ms trong khi lần lặp thứ hai sử dụng StringBuilder mất 10 ms.

Theo tôi, việc sử dụng StringBuilder nhanh hơn, nhanh hơn rất nhiều.

19
Diizzy

Có, StringBuilder cho hiệu suất tốt hơn trong khi thực hiện thao tác lặp lại qua chuỗi. Đó là bởi vì tất cả các thay đổi được thực hiện cho một phiên bản duy nhất nên có thể tiết kiệm rất nhiều thời gian thay vì tạo một phiên bản mới như String.

Chuỗi Vs Stringbuilder

  • String

    1. trong không gian tên System
    2. trường hợp bất biến (chỉ đọc)
    3. hiệu suất suy giảm khi thay đổi liên tục của giá trị xảy ra
    4. chủ đề an toàn
  • StringBuilder (chuỗi có thể thay đổi)

    1. trong không gian tên System.Text
    2. trường hợp đột biến
    3. cho thấy hiệu suất tốt hơn vì những thay đổi mới được thực hiện cho thể hiện hiện tại

Rất khuyến khích bài viết mob dotnet: String Vs StringBuilder trong C # .

Câu hỏi tràn ngăn xếp liên quan: Khả năng biến đổi của chuỗi khi chuỗi không thay đổi trong C #? .

11
Shamseer K

StringBuilder giảm số lượng phân bổ và bài tập, với chi phí sử dụng thêm bộ nhớ. Được sử dụng đúng cách, nó có thể loại bỏ hoàn toàn nhu cầu của trình biên dịch để phân bổ các chuỗi lớn hơn và lớn hơn cho đến khi kết quả được tìm thấy.

string result = "";
for(int i = 0; i != N; ++i)
{
   result = result + i.ToString();   // allocates a new string, then assigns it to result, which gets repeated N times
}

vs.

String result;
StringBuilder sb = new StringBuilder(10000);   // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
   sb.Append(i.ToString());          // fill the buffer, resizing if it overflows the buffer
}

result = sb.ToString();   // assigns once
8
moswald

Trình tạo chuỗi Vs Chuỗi:

Điều đầu tiên bạn phải biết rằng trong hội nào hai lớp này sống?

Vì thế,

chuỗi có trong không gian tên System.

StringBuilder có trong không gian tên System.Text.

Đối với khai báo chuỗi :

Bạn phải bao gồm không gian tên System. một cái gì đó như thế này Using System;

Đối với khai báo StringBuilder :

Bạn phải bao gồm không gian tên System.text. một cái gì đó như thế này Using System.text;

Bây giờ hãy đến câu hỏi thực tế.

Sự khác biệt giữa chuỗi & StringBuilder là gì?

Sự khác biệt chính giữa hai điều này là:

chuỗi là bất biến.

StringBuilder có thể thay đổi.

Vì vậy, bây giờ hãy thảo luận về sự khác biệt giữa bất biến có thể thay đổi

Mutable: : có nghĩa là Có thể thay đổi.

Bất biến: : có nghĩa là Không thể thay đổi.

Ví dụ:

using System;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // String Example

            string name = "Rehan";
            name = name + "Shah";
            name = name + "RS";
            name = name + "---";
            name = name + "I love to write programs.";

            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah RS --- I love to write programs."
        }
    }
}

Vì vậy, trong trường hợp này, chúng ta sẽ thay đổi cùng một đối tượng 5 lần.

Vì vậy, câu hỏi rõ ràng là vậy! Điều gì thực sự xảy ra dưới mui xe, khi chúng ta thay đổi cùng một chuỗi 5 lần.

Đây là những gì xảy ra khi chúng ta thay đổi cùng một chuỗi 5 lần.

hãy nhìn vào hình.

enter image description here

Giải thích:

Khi chúng tôi lần đầu tiên khởi tạo biến "name" này thành "Rehan" i-e string name = "Rehan", biến này được tạo trên stack "name" và trỏ đến giá trị "Rehan" đó. sau khi dòng này được thực thi: "name = name +" Shah ". biến tham chiếu không còn trỏ đến đối tượng đó" Rehan "mà bây giờ nó trỏ đến" Shah ", v.v.

Vì vậy, string có nghĩa là bất biến mà một khi chúng ta tạo đối tượng trong bộ nhớ, chúng ta không thể thay đổi chúng.

Vì vậy, khi chúng ta kết hợp biến name, đối tượng trước đó vẫn còn đó trong bộ nhớ và một đối tượng chuỗi mới khác sẽ được tạo ...

Vì vậy, từ hình trên, chúng ta có năm đối tượng, bốn đối tượng bị vứt đi, chúng hoàn toàn không được sử dụng. Họ vẫn còn trong bộ nhớ và họ chiếm số lượng bộ nhớ. "Garbage Collector" chịu trách nhiệm về việc làm sạch tài nguyên từ bộ nhớ.

Vì vậy, trong trường hợp chuỗi bất cứ lúc nào khi chúng ta thao tác chuỗi nhiều lần, chúng ta có một số đối tượng được tạo Ans ở đó trong bộ nhớ.

Vì vậy, đây là câu chuyện của chuỗi biến.

Bây giờ, hãy nhìn về phía StringBuilder Object. Ví dụ:

using System;
using System.Text;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // StringBuilder Example

            StringBuilder name = new StringBuilder();
            name.Append("Rehan");
            name.Append("Shah");
            name.Append("RS");
            name.Append("---");
            name.Append("I love to write programs.");


            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah Rs --- I love to write programs."
        }
    }
}

Vì vậy, trong trường hợp này, chúng ta sẽ thay đổi cùng một đối tượng 5 lần.

Vì vậy, câu hỏi rõ ràng là vậy! Điều gì thực sự xảy ra dưới mui xe, khi chúng tôi thay đổi cùng một StringBuilder 5 lần.

Đây là những gì xảy ra khi chúng ta thay đổi cùng một StringBuilder 5 lần.

hãy nhìn vào hình. enter image description here

Giải thích: Trong trường hợp đối tượng StringBuilder. bạn sẽ không có được đối tượng mới. Cùng một đối tượng sẽ được thay đổi trong bộ nhớ, vì vậy ngay cả khi bạn thay đổi đối tượng và nói 10.000 lần, chúng ta vẫn sẽ chỉ có một đối tượng stringBuilder.

Bạn không có nhiều đối tượng rác hoặc các đối tượng StringBuilder không tham số vì tại sao nó có thể thay đổi. Nó có thể thay đổi có nghĩa là nó thay đổi theo thời gian?

Sự khác biệt:

  • Chuỗi có mặt trong không gian tên Hệ thống trong đó Stringbuilder hiện diện trong không gian tên System.Text.
  • chuỗi là bất biến trong đó StringBuilder là mutabe.
4
Rehan Shah

Hiệu năng của hoạt động nối cho đối tượng String hoặc StringBuilder phụ thuộc vào tần suất phân bổ bộ nhớ xảy ra. Hoạt động nối chuỗi luôn phân bổ bộ nhớ, trong khi đó hoạt động nối chuỗi StringBuilder chỉ cấp phát bộ nhớ nếu bộ đệm đối tượng StringBuilder quá nhỏ để chứa dữ liệu mới. Do đó, lớp String thích hợp hơn cho hoạt động nối nếu một số đối tượng Chuỗi cố định được nối. Trong trường hợp đó, các hoạt động nối riêng lẻ thậm chí có thể được kết hợp thành một hoạt động duy nhất bởi trình biên dịch. Một đối tượng StringBuilder thích hợp hơn cho hoạt động nối nếu một số chuỗi tùy ý được nối; ví dụ: nếu một vòng lặp nối một số chuỗi ngẫu nhiên đầu vào của người dùng.

Nguồn: MSD

4
user487069

StringBuilder tốt hơn cho việc xây dựng một chuỗi từ nhiều giá trị không cố định.

Nếu bạn đang xây dựng một chuỗi từ nhiều giá trị không đổi, chẳng hạn như nhiều dòng giá trị trong tài liệu HTML hoặc XML hoặc các đoạn văn bản khác, bạn có thể thoát khỏi việc chỉ nối vào cùng một chuỗi, vì hầu hết tất cả các trình biên dịch đều làm "gập liên tục", một quá trình giảm cây phân tích cú pháp khi bạn có một loạt các thao tác liên tục (nó cũng được sử dụng khi bạn viết một cái gì đó như int minutesPerYear = 24 * 365 * 60). Và đối với các trường hợp đơn giản với các giá trị không cố định được nối với nhau, trình biên dịch .NET sẽ giảm mã của bạn thành một cái gì đó tương tự như những gì StringBuilder làm.

Nhưng khi phần bổ sung của bạn không thể được giảm xuống một cái gì đó đơn giản hơn bởi trình biên dịch, bạn sẽ muốn có một StringBuilder. Như fizch chỉ ra, điều đó có nhiều khả năng xảy ra bên trong một vòng lặp.

3
JasonTrue
2
Jim G.

Sử dụng các chuỗi để nối có thể dẫn đến độ phức tạp thời gian chạy theo thứ tự O(n^2).

Nếu bạn sử dụng StringBuilder, việc sao chép bộ nhớ sẽ được thực hiện ít hơn rất nhiều. Với StringBuilder(int capacity), bạn có thể tăng hiệu suất nếu bạn có thể ước tính số lượng String cuối cùng sẽ lớn đến mức nào. Ngay cả khi bạn không chính xác, có lẽ bạn sẽ chỉ phải tăng khả năng StringBuilder một vài lần có thể giúp hiệu suất cũng được.

2
Steve g

Tôi đã thấy hiệu suất tăng đáng kể từ việc sử dụng lệnh gọi phương thức EnsureCapacity(int capacity) trên một thể hiện của StringBuilder trước khi sử dụng nó cho bất kỳ lưu trữ chuỗi nào. Tôi thường gọi nó trên dòng mã sau khi khởi tạo. Nó có tác dụng tương tự như khi bạn khởi tạo StringBuilder như thế này:

var sb = new StringBuilder(int capacity);

Cuộc gọi này phân bổ bộ nhớ cần thiết trước thời hạn, điều này gây ra việc phân bổ bộ nhớ ít hơn trong nhiều hoạt động Append(). Bạn phải đưa ra một phỏng đoán có giáo dục về số lượng bộ nhớ bạn sẽ cần, nhưng đối với hầu hết các ứng dụng, điều này không quá khó. Tôi thường mắc lỗi về phía bộ nhớ quá nhiều (chúng ta đang nói 1k hoặc hơn).

2
Jason Jackson

Tôi tin rằng StringBuilder nhanh hơn nếu bạn có nhiều hơn 4 chuỗi bạn cần nối lại với nhau. Thêm vào đó, nó có thể làm một số thứ hay ho như AppendLine.

2
Gilligan

Trong .NET, StringBuilder vẫn nhanh hơn so với nối thêm chuỗi. Tôi khá chắc chắn rằng trong Java, họ chỉ tạo StringBuffer dưới mui xe khi bạn nối các chuỗi, vì vậy thực sự không có sự khác biệt. Tôi không chắc tại sao họ chưa làm điều này trong .NET.

2
Eric Z Beard

String và StringBuilder thực sự là cả hai bất biến, StringBuilder đã tích hợp sẵn bộ đệm cho phép kích thước của nó được quản lý hiệu quả hơn. Khi StringBuilder cần thay đổi kích thước là khi nó được phân bổ lại trên heap. Theo mặc định, nó có kích thước đến 16 ký tự, bạn có thể đặt cái này trong hàm tạo.

ví dụ.

StringBuilder sb = new StringBuilder (50);

1
capgpilk

Hơn nữa với các câu trả lời trước, điều đầu tiên tôi luôn làm khi nghĩ về các vấn đề như thế này là tạo ra một ứng dụng thử nghiệm nhỏ. Trong ứng dụng này, thực hiện một số thử nghiệm thời gian cho cả hai kịch bản và tự mình xem cái nào nhanh hơn.

IMHO, nối thêm 500 mục nhập chuỗi chắc chắn nên sử dụng StringBuilder.

1
RichS

Chuỗi nối sẽ chi phí bạn nhiều hơn. Trong Java, Bạn có thể sử dụng StringBuffer hoặc StringBuilder dựa trên nhu cầu của bạn. Nếu bạn muốn triển khai an toàn và đồng bộ hóa luồng, hãy truy cập StringBuffer. Điều này sẽ nhanh hơn so với nối chuỗi.

Nếu bạn không cần triển khai an toàn đồng bộ hóa hoặc luồng, hãy truy cập StringBuilder. Điều này sẽ nhanh hơn so với nối chuỗi và cũng nhanh hơn StringBuffer vì chúng không có chi phí đồng bộ hóa.

1
raffimd

StringBuilder hiệu quả hơn đáng kể nhưng bạn sẽ không thấy hiệu suất đó trừ khi bạn đang thực hiện một số lượng lớn sửa đổi chuỗi.

Dưới đây là một đoạn mã nhanh để đưa ra một ví dụ về hiệu suất. Như bạn có thể thấy bạn thực sự chỉ bắt đầu thấy sự gia tăng hiệu suất lớn khi bạn có được các bước lặp lớn.

Như bạn có thể thấy 200.000 lần lặp mất 22 giây trong khi 1 triệu lần lặp sử dụng StringBuilder gần như ngay lập tức.

string s = string.Empty;
StringBuilder sb = new StringBuilder();

Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());

for (int i = 0; i <= 50000; i++)
{
    s = s + 'A';
}

Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();

Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());

for (int i = 0; i <= 200000; i++)
{
    s = s + 'A';
}

Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());

for (int i = 0; i <= 1000000; i++)
{
    sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());

Console.ReadLine();

Kết quả của đoạn mã trên:

Chuỗi bắt đầu + lúc 28/01/2013 16:55:40.

Kết thúc chuỗi + lúc 28/01/2013 16:55:40.

Chuỗi bắt đầu + lúc 28/01/2013 16:55:40.

Kết thúc chuỗi + lúc 28/01/2013 16:56:02.

Bắt đầu Sb nối vào ngày 28/01/2013 16:56:02.

Kết thúc Sb nối vào ngày 28/01/2013 16:56:02.

0
CathalMF

Theo nguyên tắc chung, nếu tôi phải đặt giá trị của chuỗi nhiều lần hoặc nếu có bất kỳ phần phụ nào vào chuỗi, thì nó cần phải là trình tạo chuỗi. Tôi đã thấy các ứng dụng mà tôi đã viết trong quá khứ trước khi tìm hiểu về các nhà xây dựng chuỗi đã có một dấu chân bộ nhớ khổng lồ dường như tiếp tục phát triển và tăng trưởng. Việc thay đổi các chương trình này để sử dụng trình tạo chuỗi đã cắt giảm đáng kể việc sử dụng bộ nhớ. Bây giờ tôi thề bởi người xây dựng chuỗi.

0
user13288

Cách tiếp cận của tôi luôn là sử dụng StringBuilder khi nối 4 chuỗi trở lên OR Khi tôi không biết làm thế nào có thể xảy ra sự ghép nối.

Bài viết liên quan đến hiệu suất tốt ở đây

0
JohnC

StringBuilder có lẽ là thích hợp hơn. Lý do là nó phân bổ nhiều không gian hơn mức cần thiết hiện tại (bạn đặt số lượng ký tự) để chừa chỗ cho các phụ lục trong tương lai. Sau đó, những phụ kiện tương lai phù hợp với bộ đệm hiện tại không yêu cầu bất kỳ phân bổ bộ nhớ hoặc bộ sưu tập rác nào, có thể tốn kém. Nói chung, tôi sử dụng StringBuilder để nối chuỗi phức tạp hoặc định dạng nhiều, sau đó chuyển đổi thành Chuỗi bình thường khi dữ liệu hoàn tất và tôi muốn một đối tượng bất biến một lần nữa.

0
deemer

Nếu bạn đang thực hiện nhiều phép nối chuỗi, hãy sử dụng StringBuilder. Khi bạn nối với một Chuỗi, bạn tạo một Chuỗi mới mỗi lần, sử dụng thêm bộ nhớ.

Alex

0
Alex Fort