Học tập‎ > ‎C#‎ > ‎

Dữ liệu mảng trong C#

Không thể tìm thấy URL thông số tiện ích
Người đăng bài : duaca

Dữ liệu mảng trong C#

Về quan niệm, mảng là tập hợp các phần tử chứa dữ liệu cùng dạng, cùng loại, lưu trữ cho cùng một mục đích trong chương trình. Với 1 mảng đã được khai báo và cấp phát trong bộ nhớ thì các phần tử của mảng luôn được lưu trữ một cách tuần tự, liên tục, chính vì thế ta có thể truy xuất đến từng thành phần của mảng thông qua chỉ số (index) của nó. Trong C# chỉ số của mảng thường là 1 giá trị thuộc kiểu số nguyên mô tả cho thứ tự của phần tử trong mảng chứa nó, phần tử đầu tiên của mảng sẽ có chỉ số là 0, phần tử thứ 2 sẽ có chỉ số là 1, … cho đến phần tử thứ n sẽ có chỉ số là n-1.

Giả thiết ta có 1 mảng với kích thước là 50, chứa thông tin là tên của các học sinh. Bạn có thể liên tưởng đến mảng như hình vẽ mô tả dưới đây

 

 

Như vậy ta thấy trong phạm vi của mảng, các phần tử phân biệt với nhau dựa vào chỉ số, phần dữ liệu chính là nội dung chứa trong mỗi phần tử thuộc mảng.

Sử dụng mảng trong những tình huống nào ?

Chúng ta hãy nhớ lại 1 ví dụ đã đề cập đến trong phần minh họa cho sử dụng cấu trúc if ở bài trước. Khi cần so sánh để tìm số lớn nhất trong 3 số đã nhập, ta xem lại đoạn code sau:

    int max = a;       // Giả thiết số a lớn nhất
    if (b > max)       // Kiểm tra giả thiết, nếu giả thiết là sai
        max = b;       // thì tới đây ta biết được a, b số nào lớn nhất
    if (c > max)       // tương tự, đến đây ta xác định được
        max = c;       // số lớn nhất trong 3 số đã nhập

Theo giải thuật giả định ở trên, với 3 biến, bạn phải sử dụng 3 lệnh, vậy trong trường hợp có nhiều hơn 3 biến, giả sử là 4 thì theo bạn sẽ phải dùng bao nhiêu lệnh ?, Hoặc giả như trong trường hợp có 100 giá trị, bạn sẽ dùng bao nhiêu lệnh để tìm ra được phần tử lớn nhất, nhỏ nhất ?.

Nhược điểm lớn nhất khi sử dụng biến để minh họa cho bài toán trên (“bài toán tìm số lớn nhất”) chính là sự rời rạc trong lưu trữ của biến giá trị, chính vì thế chúng ta rất khó để có thể áp dụng 1 giải thuật tốt cho việc phối hợp xử lý thông tin giữa các biết cần xét.

Hãy thử nhìn nhận thế này, chúng ta có n số cần xét, các số cần xét đều có chung 1 mục đích chính là đem so sánh với nhau để biết số nào lớn hơn, hơn nữa các số đều cùng 1 kiểu giá trị (có thể cùng là int, long hay float) vậy chúng ta sẽ coi chúng như 1 mảng có n phần tử, lúc này ta có thể tiến hành lặp từ phần tử đầu tiên đến phần tử cuối cùng, xét để tìm ra phần tử lớn nhất trong n phần tử. Như vậy số phần tử nhiều hay ít (n lớn hay nhỏ) vẫn không hề ảnh hưởng đến mã lệnh của chương trình.

Trên thực tế, trong những tình huống cụ thể nhất định, việc chọn kiểu dữ liệu cho lưu trữ thông tin, giá trị trong chương trình sẽ làm ảnh hưởng khá lớn đến giải thuật của chương trình đó. Trong minh họa ở trên, chủ yếu muốn để các bạn thấy được ưu điểm khi sử dụng mảng cho bài toán tìm số lớn nhất, trong những tình huống khác, các bạn cần phải biết nhiều cấu trúc dữ liệu khác của C# để ứng dụng trong chương trình cho linh hoạt và phù hợp, điều này chúng ta sẽ cùng nhau nghiên cứu trong những bài viết sau. Bây giờ chúng ta lại quay về với dữ liệu mảng, đó chính là mục tiêu chính của bài viết này.

Cú pháp

<kiểu_dữ_liệu>[] <tên_mảng>;                                    (1)
<kiểu_dữ_liệu>[] <tên_mảng> = new <kiểu_dữ_liệu>[<Số_pt>];      (2)
<kiểu_dữ_liệu>[] <tên_mảng> = {<gt1>, <gt2>, …, <gtn>};         (3)

Trong mô tả cú pháp ở trên, chúng ta có thể khai báo và sử dụng mảng cho chương trình của mình theo 3 cách khác nhau

-         Cú pháp thứ nhất sẽ cho phép bạn khai báo 1 mảng thuộc kiểu dữ liệu xác định
Ví dụ:
Khai báo 1 mảng tham chiếu đến kiểu dữ liệu số thực tên là : diemThi
         
float[] diemThi;
Hay như khai báo 2 mảng tham chiếu đến kiểu dữ liệu số nguyên dùng để chứa hoành độ, tung độ trên màn hình tên là x, y
         
int[] x;
     int[] y;

-         Cú pháp thứ hai cho phép bạn khai báo và khởi tạo 1 mảng thông qua từ khóa new
Ví dụ:
Khai báo 1 mảng số nguyên tên là thu có 38 phần tử
         
int[] thu  = new  int[38];
Hoặc khai báo 1 mảng kiểu char có 26 phần tử
         
char[] kt = new  char[26];

-          Cú pháp thứ ba cho phép bạn có thể khai báo 1 mảng có số phần tử tương ứng với các giá trị được liệt kê trong tập hợp, như mô tả của ví dụ sau
Ví dụ:
Khai báo 1 mảng chuỗi có chứa các phần tử kiểu string lưu trữ các thứ trong 1 tuần
         
string[] thuTT = {“Hai”,“Ba”,“Tư”,“Năm”,“Sáu”,“Bảy”,“Chủ Nhật”};
Khai báo 1 mảng chứa dữ liệu chuỗi mô tả cho các ký số từ 0 .. 9 dưới dạng ngôn ngữ (Tiếng việt)
         
string[] chuSo = {“không”, ”một”, ”hai”, ”ba”, ”bốn”, ”năm”, ”sáu”, ”bảy”, ”tám”, ”chín”};

Lưu ý:

Với 1 mảng được khai báo và khởi tạo bởi từ khóa new thì mặc nhiên các phần tử của mảng sẽ được khởi gán giá trị sau khi tạo, bảng dữ liệu sau mô tả chi tiết cho bạn biết ứng với mỗi kiểu dữ liệu, giá trị khởi gán đối với  phần tử của mảng sẽ là bao nhiêu

Một số ứng dụng “nho nhỏ” cho dữ liệu kiểu mảng

Bài toán thứ 1:

      Viết chương trình, khi thi hành sẽ đọc 1 giá trị số thuộc kiểu nguyên, sau đó in ra trên màn hình chuỗi thông báo có dạng như sau


               Nhập số : 1024
               Đọc là : Một Không Hai Bốn

-          Về cơ bản thì chương trình này chẳng có gì “ghê gớm” (nếu không muốn nói là “quá dễ) nhưng tôi dùng nó để minh họa cho việc sử dụng dữ liệu kiểu mảng

-          Mã lệnh của chương trình như sau:

Hãy thử liên tưởng, nếu bạn không dùng mảng thì bạn sẽ ứng dụng cấu trúc if hay case như thế nào cho bài toán này ?

Bài toán thứ 2:

Giống như ví dụ trước, đây cũng là 1 ví dụ đơn giản nhưng hơi đặc biệt 1 chút là nó liên quan đến lịch (âm lịch). Chúng ta sẽ viết chương trình cho phép nhập vào 1 số nguyên dương tượng trưng cho năm sinh của 1 người, chương trình sẽ tính và in ra năm sinh theo âm lịch của người đó.

Để giải quyết vấn đề này, tôi có một số thông tin như sau để giúp bạn biết về đặc trưng của năm âm lịch (Nếu bạn chưa biết về những vấn đề này). Theo âm lịch, 1 năm sẽ là sự kết hợp của “Can” và “Chi” (Còn gọi là Thiên Can, Địa Chi),  Các giá trị của can và chi được liệt kê tuần tự như mô tả sau

- Can : “Giáp, Ất, Bính, Đinh, Mậu, Kỷ, Canh, Tân, Nhâm, Quý”                                  (10 Giá trị)

- Chi : “Tý, Sửu, Dần, Mão, Thìn, Tị, Ngọ, Mùi, Thân, Dậu, Tuất, Hợi”                         (12 Giá trị)

Vấn đề là làm sao biết được tương ứng với năm x thì can sẽ được chọn ra sao, chi sẽ là chi nào ?. Theo như mô tả về các giá trị ở trên, ta biết chu kỳ tuần hoàn để ứng dụng khi tính một can là 10, tức là sau 10 năm sẽ quay lại can đã sử dụng trước đó, như thế ta sẽ lấy 1 năm mà ta biết chính xác rằng trên thực tế người ta dùng can gì để suy ra cách sử dụng cho những can còn lại. Tôi chọn năm 1968, chúng ta biết năm 1968 là năm: “Mậu Thân”, như vậy tôi lấy 1968 chia cho 10 dư 8 và nếu tôi sử dụng mảng dữ liệu để lưu 10 phần tử can của chúng ta thì thứ tự của các phần tử trên mảng sẽ như sau

 

 

Tương tự như vậy, tôi lấy 1968 chia cho 12 (Chu kỳ của Chi) dư 0, tôi xác định được cách phân bổ chi trên mảng như sau

 

 

Như vậy dựa vào phân tích ở trên, ta có mã lệnh triển khai bằng C# như sau:

Phân loại đối với dữ liệu mảng

Single – dimensional arrays: Với dữ liệu mảng mà chúng ta cùng làm quen trong phần học ở trên, người ta thường gọi là mảng một chiều (mảng đơn chiều), mảng ở dạng này chỉ quan tâm đến 1 hướng truy xuất các phần tử có trong nó và mảng một chiều được cấp phát trong bộ nhớ như những dòng dữ liệu đơn lẻ. Để truy xuất đến các phần tử của mảng một chiều, ta sử dụng cặp dấu “[]” kết hợp với giá trị số (nguyên dương) để chỉ ra vị trí của phần tử muốn tác động thuộc mảng. Hãy nhớ chỉ số của phần tử đầu tiên thuộc mảng là 0 và phần tử cuối cùng có chỉ số là n-1 (Với n chính là số phần tử của mảng đã cấp phát)

(Hình minh họa mảng 1 chiều)

Rectangular – dimensional arrays: Cũng gần giống như mảng một chiều mà chúng ta đã biết, tuy nhiên “Rectangular array” là sự tích hợp của 2 mảng 1 chiều để chứa thông tin dưới dạng bảng (Bao gồm nhiều dòng và nhiều cột). Để truy xuất đến các phần tử của mảng “Rectangular” ta cần sử dụng dấu phẩy “,” trong cặp dấu “[ , ]” để mô tả hướng truy xuất thông tin, trong đó thành phần thứ nhất (bên trái dấu phẩy) tượng trưng cho chiều truy xuất thứ nhất (Dòng – Row), thành phần thứ hai (bên phải dấu phẩy) tượng trưng cho chiều thứ 2 (Cột - Column) để truy xuất đến phần tử chứa dữ liệu cụ thể (Cell in Table). Hãy quan sát 1 bảng tính của Microsoft Excel, bạn sẽ liên tưởng đến việc lưu trữ và sử dụng mảng “Rectangular” một cách trực quan hơn

Cú pháp khai báo đối với mảng thuộc dạng này cũng tương như đối với cú pháp khai báo mảng 1 chiều đã biết

<kiểu_dữ_liệu>[,] <tên_mảng>;                                     
<kiểu_dữ_liệu>[,] <tên_mảng> = new <kiểu_dữ_liệu>[<Số_pt_c1>,<Số_pt_c2>];    

Ví dụ :

Ta khai báo 1 mảng 2 chiều có sức chứa với chiều thứ nhất 5 phần tử, chiều thứ hai 20 giá trị kiểu số nguyên

int[,]  m2 = new  int[5,20];

Khai báo 1 mảng có thể chứa được tối đa 38 tọa độ trên màn hình

long[,]  toaDo = new  long[38,2];

Ví dụ:

     Chúng ta sẽ viết 1 ví dụ dành cho việc minh họa Rectangular để lưu trữ trong bộ nhớ 1 bảng cửu chương hình vuông có dạng như hình mô tả sau

     Mã lệnh của chương trình:

Jagged arrays : cũng giống như Rectangular Array, Jagged array cũng là 1 dạng mảng đa chiều, tuy nhiên để khắc phục nhược điểm của Rectangular là số phần tử trên các chiều kể từ thứ 2 trở đi luôn có kích thước cố định, Jagged Array cho phép khai báo kích thước của chiều còn lại theo nhu cầu lưu trữ dữ liệu của thuật toán, chính vì thế có thể nói Jagged array là mảng đa chiều có nhiều kích thước khác nhau đối với các chiều kể từ thứ 2 trở đi (Jagged array còn được gọi với tên khác là “Mảng-của-Mảng” : Array-of-Arrays). Hãy nhìn vào hình mô tả dưới đây, bạn sẽ hình dung về Jagged Array dễ hơn.

Chính vì đặc điểm có khể khai báo kích thước linh hoạt cho các chiều còn lại mà Jagged array có ưu điểm là tận dụng bộ nhớ tốt hơn so với Rectangular array.

Ví dụ:

Khai báo 1 mảng dạng Jagged array với đặc điểm như sau: chiều thứ nhất 3 phần tử, trong đó phần tử thứ nhất là 1 mảng số nguyên chứa 5 giá trị lần lượt {1,3,5,7,9}, phần tử thứ 2 chứa 4 giá trị {2,4,6,8} và phần tử thứ 3 chỉ chứa 2 giá trị {-36, -38}

int[][] myJaggedArray1 = new int [][]
                        {
                           new int[] {1,3,5,7,9},
                           new int[] {2,3,6,8},
                           new int[] {-36,-28}
                        };

Phối hợp giữa Rectangular và Jagged array

int[][,] myJaggedArray2 = new int [3][,]
                         {
                           
new int[,] { {1,3}, {5,7} },
                           
new int[,] { {0,2}, {4,6}, {8,10} },
                           
new int[,] { {11,22}, {99,88}, {0,9} }
                         };

Truy xuất đến phần tử của Jagged Array

Console.Write("{0}", myJaggedArray2[0][1,0]);

Một số ứng dụng của mảng

Sử dụng foreach để duyệt các phần tử của mảng

Mảng là tập hợp hữu hạn các phần tử đã được xác định trước, chính vì thế việc sử dụng cấu trúc foreach để duyệt 1 mảng giá trị khá thuận lợi, hơn nữa việc sử dụng foreach cho quá trình duyệt mảng sẽ làm cho thuật toán “đơn giản” hơn. Hãy thực hiện 1 số ví dụ sau để thấy được ưu điểm của foreach khi làm việc với mảng

Ví dụ 1:

Viết chương trình cho phép nhập vào giá trị của n phần tử thuộc kiểu nguyên (int) sau đó in ra thông báo cho biết phần tử có giá trị lớn nhất đã nhập là bao nhiêu.

(Giả sử khi thi hành, dữ liệu nhập chính xác. Ở phần này tôi không muốn dùng trycatch để xử lý tính huống kiểm tra dữ liệu để mã lệnh đơn giản)

Ví dụ 2:

     Hãy in ra màn hình những phần tử là số chẵn trong Jagged array có nội dung như sau

     {1,4,7,10,15,18,21},
     {2,3,5,7,41},
     {21,2,11,21,6,32,41,54,18,9}

  Mã lệnh của chương trình như sau

Lớp Array của C#

Với một mảng tạo ra theo cách thông thường để chứa dữ liệu, khi cần phải thực hiện các thao tác đặc biệt trên dữ liệu kiểu mảng như sắp xếp, quy định các phần tử của mảng là chỉ đọc, … người lập trình phải tự viết mã lệnh để giải quyết các vấn đền này. C# cung cấp 1 lớp array trong namespace System với các thuộc tính và phương thức để hỗ trợ những tình huống đặc biệt cần thao tác trên dữ liệu mảng với các thuộc tính và phương thức như mô tả của các bảng dưới đây.

Thuộc tính của Array class

Phương thức của Array class

Viết 1 ví dụ minh họa cho việc sử dụng 1 object của Array class. Yêu cầu: viết 1 chương trình cho phép nhập vào tên của 5 người, sau đó sắp xếp danh sách tăng dần và in ra màn hình

Đũa cả

www.bodua.com

Tài liệu tham khảo : Giáo trình Aptec

Comments