String-vs-StringBuilder-vs-StringBuffer-in-Java-susu

Đối với các bạn đang làm việc với Java có lẽ cũng từng nghe ai đó nói rằng “Dùng StringBuilder tốt hơn”. Vậy StringBuilder có tốt hơn thật không? Bài viết này mình sẽ giải thích một số thứ xoay quanh câu hỏi này. Trước khi trả lời câu hỏi này, mình xin giải thích sơ qua một vài điểm khác nhau giữa String và StringBuilder trong Java.

String và StringBuilder.

Trong  Java, String là lớp được sử dụng rất nhiều. Kể cả Hello world. Nhưng  lớp String lại thuộc loại immutable. Nghĩa là một khi đã khởi tạo, bạn sẽ không thể sửa đổi nội dung của đối tượng String (Không xét đến việc can thiệp bằng reflection). Do đó khi muốn thay đổi giá trị của 1 biến kiểu String, bạn sẽ phải tạo một đối tượng mới và gán vào biến đó, thay vì thay đổi đối tượng cũ. Việc tạo đối tượng mới này thường tốn tài nguyên hơn so với việc  sửa lại đối tượng cũ.

Thay vì cứ phải tạo đối tượng mới mỗi khi cần thay đổi giá trị của biến kiểu String, Java cung cấp cho chúng ta lớp StringBuilder (Ngoài ra còn có lớp StringBuffer, mình không đề cập ở  đây). Lớp StringBuilder thuộc loại mutable. Nghĩa là được thay đổi giá trị bên trong đối tượng. Khi thay đổi giá trị của 1 biến kiểu StringBuilder, chúng ta không cần phải tạo lại đối  tượng StringBuilder mới.

Vậy tại sao đâu đâu người ta cũng dùng String mà không phải là StringBuider?

Trả lời ngắn gọn: Vì String mang những lợi thế của loại lớp immutable, nó phù hợp hơn trong nhiều hoàn cảnh sử dụng. Còn đối với StringBuider thì cũng chỉ là lưu tạm dữ liệu trong lúc xử lí, đến cuối cùng vẫn bị chuyển về lại thành String.

Dùng dấu cộng và dùng StringBuilder (phương thức append)

Xét 2 đoạn code dùng dấu cộng và dùng StringBuilder như sau:

Sau khi biên dịch, chúng ta nhận được bytecode giống nhau.

Vật xét về performance (hiệu năng) thì 2 đoạn code Java kể trên là như nhau. Nhưng cách dùng dấu cộng trông ngắn gọn hơn, dễ đọc hơn và đương nhiên là khi viết code sẽ viết nhanh nhanh hơn rồi.

Nếu để ý, bạn sẽ thấy đoạn bytecode kể trên dùng lớp StringBuilder để nối chuỗi.

Tại sao dùng StringBuilder tốt hơn?

Như ví dụ ở phần trên, thì mình dùng dấu cộng, trình biên dịch javac nó biên dịch thành StringBuilder. Thì performance có gì khác nhau mà có người vẫn nói StringBuilder tốt hơn?

Xét đoạn code sau:

Bytecode dịch ra tương ứng với việc dùng StringBuilder như sau:

Bạn đã để ý thấy vấn đề về performance chưa nhỉ? Mỗi lần lặp là 1 lần khởi tạo 1 đối tượng  StringBuilder và 1 lần gọi phương thức toString. Việc này sẽ làm lãng phí tài nguyên của hệ thống.

Khi chúng ta viết code dùng StringBuilder thì chúng ta không viết như vậy, chúng  ta chỉ cần khởi tạo đối tượng StringBuilder đúng 1 lần và gọi phương thức toString đúng 1 lần thôi:

Rõ ràng là tốt hơn đúng không?

Tại sao người ta dùng dấu cộng nhiều hơn là dùng StringBuilder?

Câu hỏi có vẻ như hơi bị thừa. Đương nhiên  là vì dùng dấu cộng ngắn gọn hơn rồi. Nhưng nếu chỉ xét riêng về perfomance thì có phải là dùng StringBuilder sẽ tốt hơn hoặc ít nhất là tốt bằng dấu cộng?

Chúng ta hãy cùng  xét đến đoạn code sau:

Có lẽ bạn sẽ thấy lạ là bình thường, việc cộng chuỗi sẽ tạo ra chuỗi mới (hay đối tượng mới). Làm sao mà khi nối chuỗi lại cho kết quả là 1 chuỗi đã có sẵn?

Để làm rõ điều này, hãy thử biên dịch đoạn code  String s1 = "susu" + "dev"; ra bytecode, xem thế nào nhé.

Xem nào, chỉ có 1 chuỗi duy nhất và không hề dùng StringBuilder hay bất cứ thứ gì để nối chuỗi cả. Điều đó có nghĩa là gì? Việc nối chuỗi này đã được thực hiện vào thời điểm biên dịch code. javac đã tự động nối chuỗi “susu” và “dev” và trong bytecode bạn nhận được chuỗi “susudev”, chứ không phải là lệnh nối chuỗi nữa. Còn về phần dùng StringBuilder thì vẫn được biên dịch thành code nối chuỗi bình thường. Vậy trong trường hợp này, nối chuỗi dùng dấu cộng lại  tốt hơn rồi.

Việc nối chuỗi vào thời điểm biên dịch cũng xảy ra tương tự với ví dụ sau:

 

Lời kết

Để optimaze (tối ưu hóa) chương trình của bạn, javac đã biên dịch dấu cộng nối chuỗi 1 cách linh hoạt. Tuy nhiên không phải lúc nào cũng đem lại kết quả tốt nhất. Vì vậy, khi bạn hiểu rõ nó, bạn sẽ sử dụng nó 1 cách hợp lí nhất.