Họ đang đo gì
Bạn có nghĩ được về chi phí cấp phát bộ nhớ không, và có biết trình biên dịch đã sửa giúp bạn tới đâu.
Trả lời ngắn~30 giây
Bất biến cho phép chia sẻ an toàn giữa các luồng mà không cần đồng bộ, cho phép cache hash, và cho phép string pool. Nó cũng là điều kiện an toàn: nếu String sửa được thì một tham số đường dẫn file đã kiểm tra xong vẫn có thể bị đổi trước khi dùng. StringBuilder là bộ đệm ký tự sửa được, dùng khi bạn nối nhiều lần — mỗi phép + trên String tạo một object mới.
Giải thích sâu
Chi tiết cần nói cho đúng thời đại: a + b trong một biểu thức đơn KHÔNG chậm, vì trình biên dịch dịch nó thành StringConcatFactory (từ Java 9, dùng invokedynamic), thường nhanh hơn StringBuilder viết tay. Vấn đề chỉ xuất hiện khi nối TRONG VÒNG LẶP: mỗi vòng tạo một builder mới và sao chép lại toàn bộ chuỗi, nên độ phức tạp thành O(n²).
String pool cũng hay bị kể sai. Chuỗi literal được intern tự động và nằm trong heap từ Java 7 (trước đó ở PermGen). Chuỗi tạo lúc runtime thì không tự intern, nên new String("a") == "a" là false. Gọi intern() thủ công hiếm khi đáng, và trên ứng dụng có nhiều chuỗi duy nhất nó còn phản tác dụng.
// O(n²): mỗi vòng sao chép lại toàn bộ chuỗi đã có
String s = "";
for (var row : rows) s += row.name() + ",";
// O(n): một bộ đệm, mở rộng khi cần
var sb = new StringBuilder();
for (var row : rows) sb.append(row.name()).append(',');
// Từ Java 8: rõ ràng hơn cả hai cách trên
String s = rows.stream().map(Row::name).collect(joining(","));Câu hỏi tiếp theo họ sẽ hỏi
?StringBuilder và StringBuffer khác nhau?
StringBuffer đồng bộ hoá mọi phương thức, StringBuilder thì không. Gần như luôn dùng StringBuilder, vì một bộ đệm chuỗi hiếm khi được chia sẻ giữa các luồng — mà nếu có chia sẻ thì đồng bộ ở mức phương thức cũng không đủ để đúng.
Trả lời thế này là mất điểm
- Nói “luôn dùng StringBuilder,
+lúc nào cũng chậm”. Sai từ Java 9, và người phỏng vấn Java kỳ cựu sẽ nhặt ra ngay.