Chương 15: Mảng
Tác giả: Sưu tầm
Khái quát
Mảng trong C# là các đối tượng tham chiếu; chúng được định vị ngoài không gian vùng nhớ heap thay vì trong stack. Các phần tử của một mảng được lưu trữ cùng một kiểu phần tử; nếu kiểu phần tử là một kiểu tham chiếu (như string), mảng sẽ lưu trữ các tham chiếu đến các chuỗi. Nếu phần tử có kiểu giá trị (như một kiểu số, hay kiểu struct), các phần tử được lưu trữ trực tiếp bên trong mảng. Mặt khác, một mảng có kiểu giá trị không chứa đựng những thể hiện được đóng hộp.
Các mảng được khai báo bằng cách sử dụng cú pháp sau:
<type>[] identifier;
Giá trị khởi tạo cho mảng là null. Một đối tượng mảng được tạo bằng cách sử dụng new:
int[] store = new int[50];
string[] names = new string[50];
Khi một mảng được tạo ra, ban đầu nó chứa đựng các giá trị mặc định tương ứng với kiểu của mảng. Đối với mảng store ở trên, mỗi phần tử có kiểu int với giá trị 0. Đối với mảng names, mỗi phần tử có kiểu string với giá trị null.
Khởi tạo mảng
Các mảng được khởi tạo ngay khi nó được tạo ra. Trong câu lệnh khởi tạo, new int[x] có thể được bỏ qua, và trình biên dịch sẽ xác định kích thước của mảng để định vị từ số lượng thành phần trong danh sách khởi tạo:
int[] store = {0, 1, 2, 3, 10, 12};
Dòng trên tương ứng với dòng sau:
int[] store = new int[6] {0, 1, 2, 3, 10, 12};
Mảng đa chiều và mảng răng cưa
Để lập chỉ mục các phần tử nhiều hơn một chiều, hoặc là mảng đa chiều hoặc mảng răng cưa có thể được sử dụng.
Mảng đa chiều
Mảng đa chiều có nhiều hơn một chiều:
int[,] matrix = new int[5, 2];
matrix[0, 0] = 5;
matrix[3, 1] = 10;
Mảng matrix có chiều thứ nhất là 5, và chiều thứ hai là 2. Mảng này có thể được khởi tạo bằng cách sử dụng câu lệnh như sau:
int[,] matrix = {{1, 1}, {2, 2}, {3, 5}, {4, 5}};
Mảng matrix có chiều thứ nhất là 4, và chiều thứ hai là 2.
Đôi khi mảng đa chiều còn được gọi là mảng hình chữ nhật bởi vì các phần tử có thể được ghi trong một bảng hình chữ nhật (trừ số chiều <=2). Khi mảng matrix được định vị, một vùng đơn thư được từ heap để lưu trữ đầu vào của mảng. Nó có thể được biểu diễn bằng hình 16-1.
matrix |
1 |
1 |
2 |
2 |
3 |
5 |
4 |
5 |
Hình 16-1. Lưu trữ với mảng đa chiều
Mảng răng cưa
Mảng răng cưa đơn thuần là mảng của các mảng và được gọi là mảng “răng cưa” bởi vì nó không vuông. Ví dụ:
int[][] matrix = new int[3][];
matrix[0] = new int[10];
matrix[1] = new int[11];
matrix[2] = new int[2];
matrix[0][3] = 4;
matrix[1][1] = 8;
matrix[2][0] = 5;
Ở đây, mảng matrix chỉ có một chiều với ba phần tử. Các phần tử của nó là các mảng kiểu số nguyên. Phần tử đầu là một mảng 10 số nguyên, phần tử thứ hai là một mảng 11 số nguyên, và phần tử thứ ba là một mảng 2 số nguyên.
Vì các phần tử của mảng răng cưa là mảng, nên khi mức đỉnh của mảng răng cưa được định vị, mỗi phần tử được khởi tạo là null. Do đó, mỗi phần tử phải được khởi tạo để là một mảng hợp lệ. Bởi vì điều này, nên không có cấu trúc khởi tạo cho các phần tử của mảng răng cưa. Tuy nhiên, trong trường hợp hai chiều, mã trên có thể được viết lại như sau:
int[][] matrix = {new int[5], new int[4], new int[2]};
matrix[0][3] = 4;
matrix[1][1] = 8;
matrix[2][0] = 5;
Mảng này có thể được biểu diễn như hình 16-2. Biến matrix là một tham chiếu đến một mảng ba tham chiếu đến các mảng số nguyên. Bốn thao tác định vị trong vùng heap được đòi hỏi cho mảng này.
matrix |
|
0 |
0 |
0 |
4 |
0 |
0 |
8 |
0 |
0 |
5 |
0 |
Hình 16-2. Lưu trữ với mảng răng cưa
Mảng có kiểu tham chiếu
Mảng kiểu tham chiếu có thể là cái gì đó gây bối rối, bởi vì các phần tử của mảng được khởi tạo là null thay vì theo kiểu phần tử. Ví dụ:
class Employee {
public void LoadFromDatabase(int employeeID) {
// load code here
}
}
class Test {
public static void Main() {
Employee[] emps = new Employee[3];
emps[0].LoadFromDatabase(15);
emps[1].LoadFromDatabase(35);
emps[2].LoadFromDatabase(255);
}
}
Khi LoadFromDatabase() được gọi, một ngoại lệ null sẽ phát sinh bởi vì các phần tử tham chiếu vẫn chưa được thiết đặt và do đó vẫn là null.
Lớp có thể được viết lại như sau:
class Employee {
public static Employee LoadFromDatabase(int employeeID) {
Employee emp = new Employee();
// load code here
return(emp);
}
}
class Test {
public static void Main() {
Employee[] emps = new Employee[3];
emps[0] = Employee.LoadFromDatabase(15);
emps[1] = Employee.LoadFromDatabase(35);
emps[2] = Employee.LoadFromDatabase(255);
}
}
Điều này cho phép chúng ta tạo một thể hiện và tải nó, và sau đó lưu giữ nó bên trong mảng.
Lý do các mảng không được khởi tạo là do sự hiệu quả. Nếu trình biên dịch đã thực hiện khởi tạo, nó cần làm cùng việc khởi tạo đó cho mỗi phần tử, và nếu đó không phải là khởi tạo đúng, tất cả các thao tác định vị này là lãng phí.
Chuyển đổi kiểu với mảng
Chuyển đổi kiểu được cho phép giữa các mảng dựa trên số chiều và khả năng chuyển đổi giữa các kiểu phần tử.
Một chuyển đổi kiểu mặc nhiên là được cho phép từ mảng S sang mảng T nếu các mảng có cùng số chiều, các phần tử của S có một chuyển đổi tham chiếu mặc nhiên thành kiểu phần tử của T, và cả S và T là có kiểu tham chiếu. Mặt khác, nếu mảng các tham chiếu lớp, nó có thể được chuyển đổi thành mảng có kiểu cơ sở của lớp.
Chuyển đổi tường minh cũng có các yêu cầu tương tự, ngoại trừ các phần tử của S phải có thể chuyển đổi tường minh thành kiểu thành phần của T.
sing System;
class Test {
public static void PrintArray(object[] arr) {
foreach (object obj in arr)
Console.WriteLine(“Word: {0}”, obj);
}
public static void Main() {
string s = “I will not buy this record, it is scratched.”;
char[] separators = {‘ ‘};
string[] words = s.Split(separators);
PrintArray(words);
}
}
Trong ví dụ này, mảng kiểu string words có thể được chuyển như một mảng kiểu object, bởi vì mỗi phần tử kiểu string có thể được chuyển đổi thành kiểu object.
Kiểu System.Array
Bởi vì mảng trong C# là dựa trên kiểu System.Array trong .NET Runtime, nên có vài thao tác có thể thực hiện với chúng mà không được hỗ trợ theo truyền thống bằng các kiểu mảng.
Sắp xếp và tìm kiếm
Khả năng sắp xếp và tìm kiếm được xây dựng trong kiểu System.Array. Hàm Sort() sẽ sắp xếp các phần tử của một mảng, và các hàm IndexOf(), LastIndexOf(), và BinarySearch() được sử dụng để tìm kiếm các đối tượng trong mảng.
Các hàm này làm việc với kiểu cơ sở. Để có thể sử dụng chúng với các lớp người sử dụng định nghĩa và cấu trúc, hãy xem Chương 27, “Làm quen với .NET Frameworks”.
Đảo ngược
Gọi hàm Reverse() đơn giản đảo ngược tất cả các phần tử của mảng:
using System;
class Test {
public static void Main() {
int[] arr = {5, 6, 7};
Array.Reverse(arr);
foreach (int value in arr) {
Console.WriteLine(“Value: {0}”, value);
}
}
}
Mã trên cho ra kết quả sau:
7
6
5