Câu trả lời:
Vâng, 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ễ dàng hơn nhiều so với cách khác! Tuy nhiên, khi 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 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 thiết.
Để 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 bằng cách sử dụng các 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 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();
a
là một biến cục bộ và đối tượng mà nó tham chiếu chưa được gán cho một biến khác (có thể truy cập được vào một luồng khác), một trình tối ưu hóa tốt có thể xác định rằng a
không được truy cập bởi bất kỳ mã nào khác trong chuỗi này dòng; chỉ giá trị cuối cùng của a
vấn đề. Vì vậy, nó có thể coi ba dòng mã đó như thể chúng được viết a = b + c + d;
.
StringBuilder tốt hơn là NẾU 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 khai báo chuỗi SINGLE , thì điều đó hiệu quả 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ạ!)
Một ví dụ đơn giản để chứng minh sự khác biệt về tốc độ khi sử dụng String
phép nối 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 thì việc sử dụng StringBuilder
nhanh hơn, nhanh hơn rất nhiều.
Đ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.
i.ToString()
. Vì vậy, với StringBuilder, bạn vẫn phải tạo các chuỗi 6 + 1; nó giảm tạo 13 chuỗi để tạo 7 chuỗi. Nhưng đó vẫn là cách nhìn sai lầm; việc tạo ra chuỗi sáu số không liên quan. Dòng dưới cùng: đơn giản là bạn không nên đề cập đến sáu chuỗi được tạo bởi i.ToString()
; chúng không phải là một phần của so sánh hiệu quả.
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 một chuỗi. Đó là bởi vì tất cả các thay đổi được thực hiện cho một thể hiện duy nhất nên nó có thể tiết kiệm rất nhiều thời gian thay vì tạo một thể hiện mới như thế nào String
.
String
System
không gian tênStringBuilder
(chuỗi đột biến)
System.Text
không gian tênRấ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 #? .
Trình tạo chuỗi Vs Chuỗi:
Điều đầu tiên bạn phải biết rằng trong cuộc tập hợp hai lớp này sống?
Vì thế,
chuỗi có mặt trong System
không gian tên.
và
StringBuilder có mặt trong System.Text
không gian tên.
Đối với khai báo chuỗi :
Bạn phải bao gồm System
không gian tên. một cái gì đó như thế này
Using System;
và
Đối với khai báo StringBuilder :
Bạn phải bao gồm System.text
không gian tên. 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.
và
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 và đột biến
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 hiển nhiên 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.
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", tức là 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
ý nghĩa bất di bất dịch là 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 name
biến, đố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 cho việc dọn sạch các 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 hiển nhiên 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.
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:
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
}
so với
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
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: MSDN
StringBuilder
là tốt hơn để 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 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
.
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 a 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.
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.
Hãy xem xét ' Bi kịch đáng buồn của Nhà hát Tối ưu hóa vi mô '.
Sử dụng 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 một StringBuilder
, việc sao chép bộ nhớ phải được thực hiện ít hơn rất nhiều. Với StringBuilder(int capacity)
hiệu suất bạn có thể tăng hiệu suất nếu bạn có thể ước tính trận chung kết String
sẽ diễn ra lớn như thế 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 của StringBuilder
một vài lần mà còn có thể giúp hiệu suất.
Tôi đã thấy hiệu suất tăng đáng kể từ việc sử dụng lệnh EnsureCapacity(int capacity)
gọi phương thức trên một thể hiện 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 Append()
hoạt động. 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).
EnsureCapacity
ngay sau khi StringBuilder
khởi tạo. Đơn giản chỉ cần khởi tạo StringBuilder
như thế này : var sb = new StringBuilder(int capacity)
.
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.
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);
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.
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 để ghép 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.
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
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 giúp 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.
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 HOẶC Khi tôi không biết làm thế nào có thể nối được.
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.
StringBuilder sẽ hoạt động tốt hơn, từ điểm đứng bộ nhớ. Đối với việc xử lý, sự khác biệt về thời gian thực hiện có thể không đáng kể.