String vs StringBuilder vs StringBuffer in Java
Khi làm việc với chuỗi trong Java chúng ta có 3 options: String, StringBuilder, StringBuffer.
- Với String là immutable. Khi một chuỗi String được tạo ra sẽ không thể thay đổi được giá trị của nó.
- Với StringBuilder và StringBuffer là mutable. Nó được tạo ra để giúp cho việc thay đổi giá trị của chuỗi. Nhưng StringBuffer thread safe còn String Builder thì không.
Để làm rõ hơn chúng ta cùng tìm hiểu cách chúng hoạt động khi cộng chuỗi.
1. String Concatenation
Ta xét ví dụ sau:
String query = "SELECT username FROM" + " Users " + " WHERE 1 = 1"; query += " AND age > 20"; query += " AND gender = 1"; query = query.concat(" AND is_delete = 0");
a. String Concatenation với + operator
String query = "SELECT username FROM" + " Users " + " WHERE 1 = 1";
Với câu lệnh này thì Java compiler sẽ convert chúng thành:
String query = (new StringBuilder()).append("SELECT username FROM") .append(" Users") .append(" WHERE 1 = 1").toString();
Thay vì việc tạo ra nhiều ô chỉ nhớ khác nhau để lưu các String và sau đó cộng chúng lại với nhau. Compiler của Java đã làm việc rất hiệu quả để tối ưu được bộ nhớ.
Note: Có thể dùng + để concat các giá trị primitive khác với String. Compiler cũng sẽ convert chúng như trên.
String info = "I am " + 25 + "years old";
b. String Concatenation bằng += operator
query += " AND age > 20"; query += " AND gender = 1";
Java compiler sẽ convert chúng thành:
StringBuilder tmp; tmp = new StringBuilder(); tmp.append(query); tmp.append(" AND age > 20"); query = tmp.toString(); tmp = new StringBuilder(); tmp.append(query); tmp.append(" AND gender = 1"); query = tmp.toString();
Như vậy sẽ tạo ra rất nhiều instance của StringBuilder. Khi số lượng chuỗi cộng ít thì có thể không vấn đề. Nhưng khi số lượng lớn nên sẽ rất ảnh hưởng đến performance. (Xem kết quả thực nghiệm phía dưới)
c. String Concatenation bằng concat() operator
query = query.concat(" AND is_delete = 0");
Vì concat() của String trong Java được thực hiện bằng cách:
- Tạo ra một char Arrays mới có size bằng số ký tự của 2 chuỗi.
- Copy giá trị của chuỗi 1 và chuỗi 2 vào char Arrays mới vừa được tạo.
- Trỏ lại biến vào String mới vừa được tạo ra.
Note: Với cách dùng concat() gây tốn bộ nhớ và performance khi phải tạo 1 ô nhớ mới.
2. StringBuilder và StringBuffer
- StringBuilder/StringBuffer sử dụng char Arrays là mutable khác với char Arrays của String là immutable.
- Khi cộng chuỗi, StringBuilder/StringBuffer sẽ thực hiện việc thay đổi giá trị của char Arrays thay vì tạo ra liên tục các instance mới.
=> StringBuilder/StringBuffer khi cộng chuỗi sẽ tốt hơn String rất nhiều.
3. Kết luận
- Nếu có thể hãy sử dụng concat by + operator của String để có được hiệu năng và clean code tốt nhất.
- Nếu việc cộng chuỗi được lặp lại nhiều lần, ưu tiên hàng đầu là StringBuilder (StringBuffer nếu cần thread-safe).
- Hạn chế tối đa việc sử dụng += operator của String
4. Thử nghiệm
String a = ""; // StringBuilder a1 = new StringBuilder(""); for (Long i = 0L; i < 100000L; i++) { a = a.concat(i.toString()); // take 6400ms // a += b; // take 22340s // a1.append(i) // 19ms }




