Kỹ Thuật Quản Lý và Thao Tác Với Mảng Trong Java

Giới thiệu về cấu trúc mảng

Trong lập trình ứng dụng, nhu cầu lưu trữ tập hợp dữ liệu lớn luôn xuất hiện thường xuyên. Ví dụ, bạn cần quản lý danh sách điểm số của hàng trăm học sinh hoặc giá thành của nhiều mặt hàng. Nếu sử dụng từng biến riêng lẻ cho mỗi giá trị, mã nguồn sẽ trở nên cồng kềnh và khó bảo trì. Để giải quyết vấn đề này, ngôn ngữ Java cung cấp cơ chế mảng (array), cho phép gom nhóm nhiều giá trị cùng kiểu vào một đối tượng duy nhất.

Định nghĩa và bản chất

Mảng là một khối bộ nhớ liên tục dùng để lưu trữ nhiều phần tử có cùng kiểu dữ liệu. Tất cả các phần tử trong mảng đều nằm kế cận nhau trên vùng nhớ Heap, giúp tăng hiệu suất truy cập dữ liệu.

Quy trình khởi tạo

Tương tự như các tham chiếu khác, khi khai báo mảng, bạn chưa thể sử dụng ngay nó mà phải thông qua quá trình khởi tạo. Có bốn phương thức chính để tạo mới một mảng:

1. Khai báo định danh trước, gán độ dài sau

Dành cho trường hợp cần tách biệt bước định nghĩa tham chiếu với việc cấp phát bộ nhớ:

int[] dataSet;       // Bước 1: Định nghĩa tên mảng
dataSet = new int[5]; // Bước 2: Cấp phát không gian chứa 5 giá trị

2. Gộp khai báo và định hình kích thước

Làm gọn cú pháp bằng cách gộp hai dòng lệnh ở trên thành một:

float[] priceList = new float[10];
  • Bắt buộc phải chỉ định số lượng ô trống.
  • Giá trị chiều dài phải là số nguyên dương.

3. Khởi tạo dữ liệu tĩnh kèm theo

Phương thức này vừa tạo mảng vừa đưa dữ liệu vào, độ dài mảng được tính toán tự động dựa trên số lượng phần tử:

char[] codes = new char[]{'A', 'B', 'C'};

Lưu ý: Trong dấu ngoặc vuông bên trái không được ghi thêm kích thước cụ thể.

4. Cú pháp rút gọn

Sử dụng khi không cần gọi hàm new rõ ràng:

String[] names = {"User1", "User2", "User3"};

Với cách này, nếu đã có câu lệnh định nghĩa ban đầu (ví dụ String[] n;), thì không thể gán trực tiếp chuỗi giá trị vào biến đó ngay sau dấu phân cách = bằng ngoặc nhọn {} đơn thuần mà phải dùng new String[]{"..."}.

Truy cập và thao tác dữ liệu

Để tương tác với nội dung mảng, người lập trình sử dụng số thứ tự (index). Các quy tắc quan trọng bao gồm:

  • Số bắt đầu từ 0 đến tổng kích thước trừ 1.
  • Lấy giá trị: tênMảng[index]
  • Gán giá trị: tênMảng[index] = giáTri;
  • Chiều dài cố định khi tạo: Sử dụng thuộc tính tênMảng.length.

Nếu số thứ tự nằm ngoài phạm vi cho phép, chương trình sẽ bị ném ra ngoại lệ ArrayIndexOutOfBoundsException. Ví dụ minh họa:

// Tạo mảng chứa 5 phần tử
int[] ids = new int[5]; 
// Gán dữ liệu
ids[0] = 100;
ids[1] = 200;
// Truy cập lỗi sẽ gây crash
// System.out.println(ids[5]); 

// Kiểm tra độ dài
System.out.println("Tổng số phần tử: " + ids.length);

Duyệt và xử lý tập hợp

Công việc lặp qua từng phần tử để thay đổi hay đọc dữ liệu thường sử dụng vòng lặp for.

Tính tổng hoặc tìm giá trị đặc biệt

Dưới đây là ví dụ về việc duyệt toàn bộ mảng để tính tổng các phần tử tìm số lớn nhất:

int[] values = {5, 15, 25, 10, 30};
int total = 0;
int maxVal = values[0];

for (int i = 0; i < values.length; i++) {
    total += values[i];
    if (values[i] > maxVal) {
        maxVal = values[i];
    }
}
System.out.println("Tổng: " + total + " | Lớn nhất: " + maxVal);

Truyền mảng vào hàm và nhận lại kết quả

Mảng được truyền vào hàm dưới dạng tham chiếu. Hàm có thể nhận mảng vào và trả về một mảng mới tùy chỉnh.

public static void main(String[] args) {
    int[] inputData = {12, 7, 18, 24, 9};
    // Gọi hàm xử lý lấy chẵn
    int[] result = extractEven(inputData); 
    
    // In kết quả
    for(int x : result) {
        System.out.print(x + ", ");
    }
}

public static int[] extractEven(int[] src) {
    // Đếm số lượng số chẵn trước để định kích thước mảng mới
    int count = 0;
    for (int x : src) {
        if (x % 2 == 0) count++;
    }
    
    // Cấp phát mảng đích
    int[] dest = new int[count];
    int idx = 0;
    
    // Sao chép vào
    for (int x : src) {
        if (x % 2 == 0) {
            dest[idx++] = x;
        }
    }
    return dest;
}

Hiểu sâu về bộ nhớ và tham chiếu

Mô hình lưu trữ

Mảng là kiểu dữ liệu tham chiếu. Khi bạn khai báo int[] A, hệ thống lưu trữ địa chỉ của mảng ở ngăn xếp (Stack). Tại ngăn xếp Heap, dữ liệu thực sự được lưu giữ.

  • Tham chiếu kép: Khi gán A = B, địa chỉ của B được gán cho A. Cả hai biến đều trỏ về cùng một khối dữ liệu trên Heap.
  • Fresh Allocation: Mỗi lần gặp từ khóa new, hệ thống cấp phát một vùng nhớ mới trên Heap.

Mặc dù chưa khởi tạo bởi lập trình viên, các phần tử trong mảng luôn có giá trị mặc định (default value) để đảm bảo an toàn:

TypeDefault Value
int, short, byte, long0
float, double0.0
booleanfalse
Reference (Class, String, Array)null

Mở rộng kích thước mảng

Vì mảng trong Java có kích thước bất biến, để "tăng" kích thước ta phải tạo một mảng mới lớn hơn và sao chép dữ liệu cũ sang.

Thực hiện thủ công với vòng lặp

int[] oldArr = {1, 2, 3};
int newSize = oldArr.length * 2;
int[] newArr = new int[newSize];

// Sao chép từng phần tử
for(int i=0; i

Sử dụng thư viện System

Phương thức System.arraycopy giúp sao chép tối ưu hơn:

int[] buffer = new int[oldArr.length * 2];
System.arraycopy(oldArr, 0, buffer, 0, oldArr.length);
oldArr = buffer;

Sử dụng Arrays.copyOf

Trong gói java.util, phương thức copyOf trả về ngay mảng mới đã được copy đầy đủ:

import java.util.Arrays;
// ...
oldArr = Arrays.copyOf(oldArr, newSize);

Thuật toán sắp xếp dữ liệu

Việc tổ chức dữ liệu theo thứ tự là yêu cầu cốt lõi. Dưới đây là ba phương pháp phổ biến:

1. Sắp xếp nổi bọt (Bubble Sort)

So sánh cặp phần tử liền kề và đổi chỗ nếu sai thứ tự.

int[] nums = {8, 2, 9, 1};
for (int i = 0; i < nums.length - 1; i++) {
    for (int j = 0; j < nums.length - 1 - i; j++) {
        if (nums[j] > nums[j+1]) {
            int temp = nums[j];
            nums[j] = nums[j+1];
            nums[j+1] = temp;
        }
    }
}

2. Sắp xếp chọn (Selection Sort)

Luôn tìm phần tử nhỏ nhất còn lại trong đoạn chưa sắp xếp và đưa về vị trí đầu tiên.

for (int i = 0; i < nums.length - 1; i++) {
    int minIdx = i;
    for (int j = i + 1; j < nums.length; j++) {
        if (nums[j] < nums[minIdx]) {
            minIdx = j;
        }
    }
    if (minIdx != i) {
        int temp = nums[i];
        nums[i] = nums[minIdx];
        nums[minIdx] = temp;
    }
}

3. Phương thức tích hợp sẵn

Để tiết kiệm thời gian, hãy dùng công cụ có sẵn của JDK:

Arrays.sort(nums); // Mặc định sắp xếp tăng dần

Thẻ: Java array-programming Data-Structures memory-management bubble-sort

Đăng vào ngày 13 tháng 6 lúc 02:31