Trong quá trình phát triển ứng dụng, dữ liệu dạng văn bản (chuỗi ký tự) xuất hiện ở khắp mọi nơi: từ tên người dùng, địa chỉ email, mật khẩu cho đến nội dung bài viết. Ở bài học trước, chúng ta đã làm quen với mảng một chiều để lưu trữ danh sách dữ liệu. Trong bài này, chúng ta sẽ đi sâu vào cách làm chủ các phương thức xử lý chuỗi phổ biến với String và giải mã lý do tại sao nên sử dụng StringBuilder khi cần tối ưu hiệu năng bộ nhớ.
1. Lớp String Trong Java Và Tính Bất Biến (Immutable)
Trong Java, String là một lớp đặc biệt đại diện cho chuỗi ký tự. Có hai cách cơ bản để tạo một đối tượng String:
// Cách 1: Sử dụng String literal (chuỗi chữ) - Khuyên dùng trong trường hợp thông thường
String s1 = "Hello Java";
// Cách 2: Sử dụng từ khóa new
String s2 = new String("Hello Java");Khi bạn khởi tạo bằng cách 1 (String literal), Java sẽ lưu chuỗi vào một vùng nhớ đặc biệt gọi là String Pool (nằm trong bộ nhớ Heap). Nếu một chuỗi có cùng nội dung được tạo ra bằng String literal, Java sẽ tái sử dụng vùng nhớ cũ thay vì tạo mới để tiết kiệm bộ nhớ.
Đặc tính bất biến (Immutable) là gì?
Một điểm quan trọng cần lưu ý: Đối tượng String trong Java là bất biến (Immutable). Điều này có nghĩa là khi một đối tượng String đã được khởi tạo, bạn không thể thay đổi giá trị bên trong vùng nhớ của nó.
Khi bạn thực hiện thao tác nối chuỗi hoặc biến đổi chuỗi (ví dụ: chuyển thành chữ hoa), Java không sửa trực tiếp trên chuỗi cũ mà sẽ cấp phát một vùng nhớ hoàn toàn mới để chứa kết quả mới.
String str = "Xin chào";
str = str + " các bạn"; // Một đối tượng String mới được tạo ra trong bộ nhớ2. Các Phương Thức Xử Lý Chuỗi Phổ Biến Trong Class String
Lớp String trong Java cung cấp rất nhiều phương thức tiện ích giúp bạn thao tác với văn bản một cách dễ dàng:
- length(): Trả về độ dài (số lượng ký tự) của chuỗi.
- charAt(int index): Lấy ra ký tự tại vị trí (chỉ mục) chỉ định (bắt đầu từ 0).
- substring(int beginIndex, int endIndex): Cắt lấy một chuỗi con từ vị trí
beginIndexđếnendIndex - 1. - indexOf(String str): Tìm vị trí xuất hiện đầu tiên của chuỗi con trong chuỗi ban đầu. Nếu không thấy, trả về
-1. - toUpperCase() / toLowerCase(): Chuyển đổi toàn bộ chuỗi sang chữ hoa hoặc chữ thường.
- trim(): Loại bỏ các khoảng trắng thừa ở đầu và cuối chuỗi.
- equals() / equalsIgnoreCase(): So sánh nội dung của hai chuỗi (có hoặc không phân biệt chữ hoa/thường). Lưu ý: Không dùng toán tử == để so sánh nội dung chuỗi.
Ví dụ minh họa:
public class StringDemo {
public static void main(String[] args) {
String text = " Lập Trình Java ";
// Loại bỏ khoảng trắng thừa ở hai đầu
String trimmed = text.trim();
System.out.println("Sau khi trim: '" + trimmed + "'"); // "Lập Trình Java"
// Lấy độ dài
System.out.println("Độ dài: " + trimmed.length()); // 14
// Lấy chuỗi con "Java"
String sub = trimmed.substring(10);
System.out.println("Chuỗi con: " + sub); // "Java"
// Tìm vị trí chữ 'Trình'
int index = trimmed.indexOf("Trình");
System.out.println("Vị trí chữ 'Trình': " + index); // 4
}
}3. Tối Ưu Bộ Nhớ Với StringBuilder
Vì String có tính bất biến, nếu bạn thực hiện nối chuỗi liên tục (ví dụ trong vòng lặp hàng nghìn lần), chương trình sẽ phải liên tục cấp phát các vùng nhớ mới và giải phóng vùng nhớ cũ. Thao tác này tiêu tốn nhiều tài nguyên bộ nhớ và làm giảm hiệu năng của ứng dụng.
Để giải quyết vấn đề này, Java cung cấp lớp StringBuilder. Khác với String, StringBuilder là Mutable (có thể thay đổi giá trị trực tiếp trên vùng nhớ đã cấp phát mà không tạo đối tượng mới).
Lưu ý thêm: Lớp
StringBuffercũng tương tựStringBuildernhưng hỗ trợ đồng bộ (thread-safe) dùng cho ứng dụng đa luồng. Trong các ứng dụng đơn luồng hoặc xử lý biến cục bộ thông thường,StringBuilderlà lựa chọn tối ưu hơn về tốc độ.
Các phương thức thường dùng với StringBuilder:
- append(...): Thêm dữ liệu (chuỗi, số, ký tự,...) vào cuối chuỗi hiện tại.
- insert(int offset, String str): Chèn chuỗi vào vị trí chỉ định.
- delete(int start, int end): Xóa các ký tự từ vị trí
startđếnend - 1. - reverse(): Đảo ngược chuỗi.
- toString(): Chuyển đổi đối tượng StringBuilder về lại dạng String thông thường.
Ví dụ so sánh hiệu năng và cách dùng StringBuilder:
public class StringBuilderDemo {
public static void main(String[] args) {
// Tối ưu khi nối chuỗi trong vòng lặp
StringBuilder builder = new StringBuilder();
for (int i = 1; i <= 5; i++) {
builder.append("Bước ").append(i).append("; ");
}
// Chuyển kết quả về String để sử dụng
String result = builder.toString();
System.out.println(result); // Bước 1; Bước 2; Bước 3; Bước 4; Bước 5;
// Đảo ngược chuỗi
builder.reverse();
System.out.println("Chuỗi đảo ngược: " + builder.toString());
}
}4. Khi Nào Nên Dùng String Và Khi Nào Dùng StringBuilder?
Để viết code vừa sạch vừa tối ưu, bạn có thể áp dụng quy tắc đơn giản sau:
- Dùng String: Khi dữ liệu chuỗi ít bị thay đổi, hoặc chỉ thực hiện các thao tác ghép chuỗi đơn giản (với số lượng phép cộng chuỗi ít và cố định).
- Dùng StringBuilder: Khi bạn phải làm việc với các chuỗi lớn, hoặc thực hiện ghép/sửa đổi chuỗi liên tục bên trong các vòng lặp (loop).
Kết Luận
Hiểu rõ bản chất bất biến của String cùng với việc áp dụng linh hoạt các phương thức xử lý văn bản và StringBuilder sẽ giúp chương trình Java của bạn vận hành trơn tru và tối ưu bộ nhớ hơn rất nhiều. Ở bài viết tiếp theo, chúng ta sẽ chính thức bước vào một trong những chương quan trọng nhất của Java: Lập Trình Hướng Đối Tượng (OOP) - Lớp (Class) Và Đối Tượng (Object).
Nguồn tham khảo
- Hướng dẫn sử dụng Java String, StringBuffer và StringBuilder - GP Coder (Lập trình Java)
- Hướng dẫn sử dụng Java String, StringBuffer và StringBuilder
- Sử dụng subString trong xử lý văn bản trong Java | TopDev
- Sự khác biệt giữa string và StringBuilder trong C là gì
- std::string và Xử Lý Chuỗi trong C++
Nhận xét
Đăng nhận xét