Contents
Đã bao giờ bạn nghĩ rằng thứ bạn đang làm thường ngày sẽ có cách giải quyết tốt hơn. Lập trình cũng vậy, với sự phát triển hằng giờ của công nghệ thì việc tối ưu hiệu năng của hệ thống là rất cần thiết. Mĩnh nghĩ chưa có lập trình viên nào không sử dụng lớp String (C#, Java, …) nhưng không phải số lượng lớn lập trình viên biết sử dụng StringBuilder. StringBuilder là gì? Nó có gì hơn với String mà mình nên trang diendanhocweb.com lại có bài “Tại sao nên dùng StringBuilder hơn String?”
Mình thu hẹp đối tượng trong ngôn ngữ C# như sau
String là 1 lớp trong C# chuyên dùng để xử lý các vấn đề liên quan đến chuỗi.
Bạn có thể tạo đối tượng String bởi sử dụng một trong các phương thức sau:
-
- Bằng việc gán một hằng chuỗi cho một biến String
- Sử dụng một constructor của lớp String
- Sử dụng toán tử nối chuỗi (+)
- Bởi việc thu nhận một thuộc tính hoặc gọi một phương thức mà trả về một chuỗi
- Bằng việc gọi một phương thức định dạng để chuyển đổi một giá trị hoặc một đối tượng thành biểu diễn chuỗi của nó.
Các thuộc tính của lớp String trong C#
Lớp String trong C# có hai thuộc tính:
STT | Thuộc tính |
---|---|
1 | CharsLấy đối tượng Char tại một vị trí cụ thể trong đối tượng String hiện tại |
2 | LengthLấy số ký tự của đối tượng String hiện tại |
Phương thức của lớp String trong C#
Lớp String có một số phương thức mà hữu ích cho bạn trong khi làm việc với các đối tượng String trong C#. Bảng dưới đây liệt kê các phương thức được sử dụng phổ biến nhất:
STT | Phương thức |
---|---|
1 | public static int Compare(string strA, string strB)So sánh hai đối tượng String cụ thể và trả về một integer mà chỉ vị trí có liên quan của chúng trong thứ tự sắp xếp |
2 | public static int Compare(string strA, string strB, bool ignoreCase )So sánh hai đối tượng String cụ thể và trả về một integer mà chỉ vị trí có liên quan của chúng trong thứ tự sắp xếp. Tuy nhiên, nó bỏ qua sự phân biệt kiểu nếu tham số Boolean là true |
3 | public static string Concat(string str0, string str1)Nối chuỗi hai đối tượng String |
4 | public static string Concat(string str0, string str1, string str2)Nối chuỗi ba đối tượng String |
5 | public static string Concat(string str0, string str1, string str2, string str3)Nối chuỗi bốn đối tượng String |
6 | public bool Contains(string value)Trả về một giá trị chỉ dẫn có hay không đối tượng String đã cho xuất hiện bên trong chuỗi này |
7 | public static string Copy(string str)Tạo một đối tượng String mới với cùng giá trị như chuỗi đã cho |
8 | public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)Sao chép một số ký tự cụ thể từ một vị trí đã cho của đối tượng String tới một vị trí đã xác định trong một mảng các ký tự Unicode |
9 | public bool EndsWith(string value)Xác định có hay không phần kết thúc của đối tượng String là so khớp với chuỗi đã cho |
10 | public bool Equals(string value)Xác định có hay không đối tượng String hiện tại và đối tượng String đã cho là có cùng giá trị |
11 | public static bool Equals(string a, string b)Xác định có hay không hai đối tượng String đã cho có cùng giá trị |
12 | public static string Format(string format, Object arg0)Thay thế một hoặc nhiều mục định dạng trong một chuỗi đã cho với biểu diễn chuỗi của một đối tượng cụ thể |
13 | public int IndexOf(char value)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện đầu tiên của ký tự Unicode đã cho trong chuỗi hiện tại |
14 | public int IndexOf(string value)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện đầu tiên của chuỗi đã cho trong Instance (sự thể hiện) này |
15 | public int IndexOf(char value, int startIndex)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện đầu tiên của ký tự Unicode đã cho trong chuỗi này, bắt đầu tìm kiếm tại vị trí của ký tự đã cho |
16 | public int IndexOf(string value, int startIndex)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện đầu tiên của chuỗi đã cho trong Instance (sự thể hiện) này, bắt đầu tìm kiếm tại vị trí của ký tự đã cho |
17 | public int IndexOfAny(char[] anyOf)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện đầu tiên của Instance (sự thể hiện) này của bất kỳ ký tự nào trong một mảng ký tự Unicode đã xác định |
18 | public int IndexOfAny(char[] anyOf, int startIndex)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện đầu tiên của Instance (sự thể hiện) này của bất kỳ ký tự nào trong một mảng ký tự Unicode đã xác định, bắt đầu tìm kiếm tại vị trí của ký tự đã cho |
19 | public string Insert(int startIndex, string value)Trả về một chuỗi mới trong đó một chuỗi đã cho được chèn tại một vị trí có chỉ mục đã xác định trong đối tượng String hiện tại |
20 | public static bool IsNullOrEmpty(string value)Chỉ rằng có hay không chuỗi đã cho là null hoặc là một chuỗi Empty |
21 | public static string Join(string separator, params string[] value)Nối chuỗi tất cả phần tử của một mảng chuỗi, bởi sử dụng Separator (bộ tách) đã cho giữa mỗi phần tử |
22 | public static string Join(string separator, string[] value, int startIndex, int count)Nối chuỗi các phần tử đã xác định của một mảng chuỗi, bởi sử dụng Separator (bộ tách) đã cho giữa mỗi phần tử |
23 | public int LastIndexOf(char value)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện cuối cùng của ký tự Unicode đã cho bên trong đối tượng String hiện tại |
24 | public int LastIndexOf(string value)Trả về chỉ mục (dựa trên cơ sở 0) cho sự xuất hiện cuối cùng của một chuỗi đã cho bên trong đối tượng String hiện tại |
25 | public string Remove(int startIndex)Gỡ bỏ tất cả ký tự trong Instance hiện tại, bắt đầu tại vị trí đã xác định và tiếp tục tới vị trí cuối cùng, và trả về chuỗi đó |
26 | public string Remove(int startIndex, int count)Gỡ bỏ số ký tự đã cho trong chuỗi hiện tại bắt đầu tại một vị trí đã xác định và trả về chuỗi đó |
27 | public string Replace(char oldChar, char newChar)Thay thế tất cả ký tự Unicode đã cho xuất hiện trong đối tượng String hiện tại với ký tự Unicode đã xác định và trả về chuỗi mới |
28 | public string Replace(string oldValue, string newValue)Thay thế tất cả chuỗi đã cho xuất hiện trong đối tượng String hiện tại với đối tượng string đã xác định và trả về chuỗi mới |
29 | public string[] Split(params char[] separator)Trả về một mảng chuỗi mà chứa các chuỗi phụ trong đối tượng String hiện tại, được giới hạn bởi các phần tử của một mảng ký tự Unicode đã cho |
30 | public string[] Split(char[] separator, int count)Trả về một mảng chuỗi mà chứa các chuỗi phụ trong đối tượng String hiện tại, được giới hạn bởi các phần tử của một mảng ký tự Unicode đã cho. Tham số int xác định số chuỗi phụ tối đa để trả về |
31 | public bool StartsWith(string value)Xác định có hay không phần bắt đầu của instance của chuỗi này so khớp với chuỗi đã cho |
32 | public char[] ToCharArray()Trả về một mảng ký tự Unicode với tất cả ký tự trong đối tượng String hiện tại |
33 | public char[] ToCharArray(int startIndex, int length)Trả về một mảng ký tự Unicode với tất cả ký tự trong đối tượng String hiện tại, bắt đầu từ chỉ mục đã xác định và tới độ dài đã cho |
34 | public string ToLower()Trả về một bản sao của chuỗi này đã được biến đổi thành chữ thường |
35 | public string ToUpper()Trả về một bản sao của chuỗi này đã được biến đổi thành chữ hoa |
36 | public string Trim()Gỡ bỏ tất cả ký tự whitespace từ đối tượng String hiện tại |
Điểm yếu của lớp String
Tuy rằng lớp String có nhiều phương thức xử lý như vậy. Nhưng về hiệu năng có 1 điểm yếu như sau
Bạn khai báo một chuỗi:
1
2
3
4
|
String myName = "Tạp chí Lập trình" ; //Tiếp đến để thêm vào cuối chuỗi này bạn làm như sau: myName = myName + " - 2012" ; |
Khi đó, nếu xem nội dung của chuỗi myName chắc chắn bạn nhận được là “Tạp chí Lập trình – 2012”, tuy nhiên để làm được điều này trình biên dịch sẽ phải tạo một đối tượng mới có chứa nội dung là “Tạp chí Lập trình – 2012” rồi gán nó cho tham chiếu myName thay vì sửa nội dung của đối tượng ban đầu. Đến đây chắc là bạn sẽ nghĩ tới tình huống mà ta cần thực hiện các hành động như thêm nội dung, bớt nội dung, thay thế, v.v. với một đối tượng String sẽ dẫn đến phát sinh hàng loạt các đối tượng String mới trong bộ nhớ.
Tính ra nếu các bạn dùng String để thay đổi quá nhiều thì hiệu năng bị giảm bất ngờ
Để giải quyết được điểm yếu này Microsoft đã đưa ra lớp mới StringBuilder
Vậy vấn đề của chúng ta ở đây đó là hiệu suất (performance)? Quá rõ ràng rồi, nếu chúng ta thao tác nhiều với chuỗi (thay đổi nội dung) thông qua các đối tượng String thì HIỆU SUẤT chính là vấn đề mà bạn gặp phải. Tức là càng tạo ra nhiều đối tượng tạm thì bộ thu dọn rác (Garbage Collection) càng phải làm việc nhiều hơn để dọn dẹp lại bộ nhớ.
Trong trường hợp này StringBuilder hoặc StringBuffer sẽ giúp bạn giải quyết vấn đề về hiệu suất. Tại sao vậy?
Như đã khẳng định ở phần đầu, hai lớp trên đều thuộc lọai mutable, chúng ta có thể thay đổi được các giá trị của chúng, hay nói cách khác các chuỗi chứa trong StringBuffer, StringBuilder có thể thay đổi được giá trị. Nhờ đặc tính này mà khi thay đổi các chuỗi trong những đối tượng này Trình biên dịch không phải tạo các đối tượng mới (đối tượng tạm) và vấn đề về performance như kể trên với String được giải quyết triệt để ở StringBuffer, StringBuilder. Chúng ta cùng xem thao tác thay đổi nội dung một chuỗi với StringBuilder:
Tạo mới một chuỗi:
1
2
3
4
|
StringBuilder myName = new StringBuilder( "Tạp chí Lập trình" ); //Bổ sung vào cuối một chuỗi myName.append( " - 2012" ); |
Vậy là bạn đã đổi được nội dung của một chuỗi với StringBuilder mà không lo sợ về performance.
Cuối cùng, bạn có muốn biết sự khác biệt giữa StringBuilder và StringBuffer không? Hai lớp này được thiết kế với mục đích giống nhau, đó là thao tác hiệu quả với chuỗi, chúng cũng có các phương thức giống nhau để làm việc với chuỗi. Điểm khác biệt đáng lưu ý: StringBuffer thuộc loại synchronized do đó các phương thức của nó đều là “thread safe” (thích hợp với xử lý đa luồng – multi thread), trong khi StringBuilder thì ngược lại, không synchronized. Với đặc tính “thread safe”, các phương thức của StringBuffer sẽ chạy chậm hơn so với StringBuilder, vì vậy nếu không làm về Multi-thread bạn nên chọn StringBuilder thay vì chọn StringBuffer.