Mảng động
Dynamic array
Dynamic array
Bên cạnh các mảng tĩnh, delphi cũng cung cấp các mảng động, số lượng phần tử rất lớn và tùy thuộc vào bộ nhớ máy tính.
Mảng động có thể thay đổi độ dài tùy ý bằng thủ tục SetLength. Khác với mảng tĩnh được lưu trữ trên stack, mảng động được phân bố trên heap nên sẽ không bị lỗi Stack Overflow khi mảng quá lớn.
Lưu ý: Mảng động bắt đầu từ 0, không phải từ 1. Nên phần tử đầu tiên sẽ là 0, phần tử thứ 2 là 1, ...
Độ dài mảng tĩnh có thể lấy được qua hàm Length() tương tự như chuỗi string.
Khai báo giống như mảng tĩnh, nhưng không có hai chỉ số đầu và cuối. Khai báo có thể trực tiếp bằng var, hoặc gián tiếp thông qua type.
Trực tiếp:
var
A, B: array of Integer;
C: array of Char;
Gián tiếp:
type
IntArray = array of Integer;
CharArray = array of Char;
var
A, B: IntArray;
C: CharArray;
Đối với mảng nhiều chiều, bạn sử dụng cụm array of liên tiếp nhau, Delphi sẽ tự hiểu
Đoạn code dưới đây khai báo mảng động X hai chiều và mảng động Y 3 chiều.
var
X: array of array of Integer;
Y: array of array of array of Byte;
Bạn đã biết, mảng động có thể thay đổi độ dài được, và điều đó thực hiện bởi thủ tục SetLength.
var
A: array of Integer;
begin
SetLength(A, 10);
end;
Thủ tục SetLength với tham số 1 là tên mảng, các tham số sau là độ dài của mảng. Nếu mảng có N chiều, thì sẽ có thêm N tham số giá trị.
var
B: array of array of array of Integer; // Mảng 3 chiều
begin
SetLength(B, 2, 5, 10);
end;
Lưu ý: Đừng gọi SetLength quá nhiều lần. Hiệu suất ứng dụng sẽ giảm. Thay vì gọi SetLength 100 lần với từng vòng lặp, hãy gọi SetLength(100) một lần thôi.
Để lặp hết mảng động, chúng ta thường sử dụng vòng lặp for ... to ... do. Từ 0 đến Length - 1.
var
i: Integer;
A: array of integer;
begin
...
for i := 0 to Length(A) - 1 do
writeln(A[i]);
end;
Hoặc sử dụng vòng lặp for ... in.
var
i: Integer;
A: array of integer;
begin
...
for i in A do
writeln(i);
end;
Đối với mảng động, chúng ta có thêm một cách mới, sử dụng hàm Low và High làm thay chỉ số đầu và chỉ số cuối trong vòng lặp for ... to ... do
var
i: Integer;
A: array of integer;
begin
...
for i := Low(A) to High(A) do
writeln(A[i]);
end;
Hàm Low() trả về chỉ số nhỏ nhất, High() trả về chỉ số lớn nhất của mảng động A.
Một tính năng rất hay của mảng động trong delphi là khả năng gán nhanh. Giả sử, bạn có một mảng A là mảng động các số nguyên như sau
var
A: array of Integer;
Để gán cho A một số giá trị nào đó, đối với mảng tĩnh bạn phải gán từng giá trị như sau
begin
A[0] := 2;
A[1] := 3;
A[2] := 5;
A[3] := 7;
end;
Sẽ rất dài dòng và rắc rối. Với mảng tĩnh, bạn chỉ cần một dòng duy nhất.
A := [2, 3, 5, 7];
Đối với mảng các kí tự (Char) cũng vậy (tuy hơi dài dòng tí nhưng cũng đỡ hơn)
var
B: array of Char;
begin
B := ['H', 'e', 'l', 'l', 'o'];
end;
Với các hằng mảng cũng tương tự.
const
MANG: array of Integer = [1, 2, 3];
Đối với việc gán nhanh cho mảng động, bạn không cần phải SetLength trước. Trình biên dịch sẽ tự động làm điều đó.
Tương tự mảng tĩnh, nếu bạn khai báo hai mảng động giống nhau (cùng kiểu) thì bạn có thể gán mảng này sang mảng kia
var
A, B: array of Integer;
begin
A := [1, 3, 5];
B := [2, 4];
A := B;
...
end;
Sau khi gán A := B, thì lúc này, các phần tử của A là 2, 4, giống với B.
Bạn có thể dùng hàm SetLength để giải phóng bộ nhớ cho các phần tử không sử dụng. Ví dụ, mảng động A của bạn có 50 phần tử như sau
var
A: array of Integer;
begin
SetLength(A, 50);
...
end;
Khi bạn dùng hàm SetLength(A, 30); thì 30 phần tử đầu sẽ giữ nguyên, 20 phần tử cuối sẽ bị xóa, giải phóng bộ nhớ vì nó đã vượt ra ngoài phạm vi mảng động. Đây là một ưu điểm của mảng động so với mảng tĩnh, có thể tự động giải phóng bộ nhớ nhanh chóng.
Để làm rỗng toàn bộ mảng động có 2 cách sau (trên 2 dòng), cách nào cũng được nhưng mình khuyên dùng cách thứ 2 hơn.
var
A: array of Integer;
begin
...
SetLength(A, 0);
A := nil;
end;
Gán cho mảng A giá trị nil cũng có nghĩa là làm rỗng toàn bộ mảng, các phần tử được giải phóng bộ nhớ.