Bài 10: Xử Lý Chuỗi Dữ Liệu Với String Và StringBuilder Trong Java

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ớ.

Xử lý chuỗi dữ liệu với String và StringBuilder trong Java

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 đến endIndex - 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 StringBuffer cũng tương tự StringBuilder như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, StringBuilder là 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 đến end - 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


📚 Chuỗi bài: Lộ Trình Tự Học Java Từ Con Số 0 Đến Ứng Dụng Thực Tế

Chuỗi 20 bài viết thiết kế dạng 'cầm tay chỉ việc' dành riêng cho người mới bắt đầu học lập trình Java. Lộ trình đi từ tư duy nền tảng, cú pháp cơ bản, lập trình hướng đối tượng (OOP) cho đến hoàn thiện một dự án thực tế nhỏ.

  1. 1. Java Là Giống Gì? Cài Đặt Environment (JDK & IntelliJ IDEA) Cực Nhanh
  2. 2. Giải Mã Chương Trình 'Hello World' Và Cấu Trúc Cơ Bản Của Bài Code Java
  3. 3. Biến Và Kiểu Dữ Liệu Trong Java: Cách Lưu Trữ Thông Tin Đúng Chuẩn
  4. 4. Toán Tử Trong Java: Hướng Dẫn Tính Toán Và So Sánh Chi Tiết
  5. 5. Nhập Xuất Dữ Liệu Với Scanner: Làm Cho Chương Trình Biết Tương Tác
  6. 6. Cấu Trúc Điều Kiện If-Else Và Switch-Case: Dạy Máy Tính Rẽ Nhánh Tư Duy
  7. 7. Vòng Lặp For, While Và Do-While Trong Java: Tự Động Hóa Công Việc Lặp Lại
  8. 8. Phương Thức (Method) Trong Java: Tái Sử Dụng Code Một Cách Thông Minh
  9. 9. Mảng Một Chiều (Array) Trong Java: Quản Lý Danh Sách Dữ Liệu Đơn Giản
  10. 10. Bài 10: Xử Lý Chuỗi Dữ Liệu Với String Và StringBuilder Trong Java ← Bạn đang đọc
  11. 11. Lập Trình Hướng Đối Tượng (OOP): Lớp (Class) Và Đối Tượng (Object) Là Đâu?
  12. 12. Tính Đóng Gói (Encapsulation) Và Access Modifiers: Bảo Vệ Dữ Liệu Safe 100%
  13. 13. Tính Kế Thừa (Inheritance): Tái Sử Dụng Mã Nguồn Với Từ Khóa Extends
  14. 14. Tính Đa Hình (Polymorphism): Nạp Chồng (Overloading) Và Ghi Đè (Overriding)
  15. 15. Lộ Trình Tự Học Java: Tính Trừu Tượng (Abstraction) - Abstract Class Hay Interface?
  16. 16. Xử Lý Ngoại Lệ (Exception Handling): Giúp Chương Trình Không Bị 'Sập' Đột Ngột
  17. 17. Java Collections Framework: Làm Quen Mảng Cố Định Với ArrayList
  18. 18. Lưu Trữ Cặp Dữ Liệu Key-Value Cực Kỳ Nhanh Với HashMap
  19. 19. Đọc Và Ghi File Trong Java: Lưu Trữ Dữ Liệu Lâu Dài Vào Ổ Cứng
  20. 20. Dự Án Thực Tế: Xây Dựng Ứng Dụng Quản Lý Sinh Viên Console Hoàn Chỉnh

← Bài trước: Mảng Một Chiều (Array) Trong Java: Quản Lý Danh Sách Dữ Liệu Đơn Giản Bài tiếp theo: Lập Trình Hướng Đối Tượng (OOP): Lớp (Class) Và Đối Tượng (Object) Là Đâu? →

Nhận xét