Kiểu mảng

Mảng là gì?

Mảng (array) trong Java là một tập hợp các phần tử có cùng kiểu dữ liệu, có địa chỉ tiếp nhau trên bộ nhớ (memory). Mảng có số phần tử cố định và bạn không thể thay đổi kích thước của nó.

Kiểu dữ liệu trong mảng có thể là kiểu nguyên thủy (primitive type) hoặc kiểu đối tượng (object).

Mảng trong Java là dựa trên chỉ mục (index), phần tử đầu tiên của mảng được lưu trữ tại chỉ mục 0.

Ví dụ: mảng một chiều có 5 phần tử, chứa các số int

Ngoài các mảng 1 chiều, đôi khi bạn cũng phải làm việc với mảng 2 chiều hoặc nhiều chiều. Một mảng 2 chiều, các phần tử của nó sẽ được đánh hai chỉ số, chỉ số hàng và chỉ số cột. Dưới đây là hình ảnh một mảng 2 chiều.

Ví dụ: mảng hai chiều, mỗi chiều có 5 phần tử, chứa các số int

Mảng một chiều

Khai báo biến mảng trong Java

Khai báo một mảng, chưa chỉ rõ số phần tử:

Cú pháp:

Kieu_du_lieu[] ten_bien;

Kieu_du_lieu có thể là kiểu nguyên thủy (primitive) hoặc kiểu đối tượng (object).

Ví dụ: int[] arr;

Khởi tạo số lượng phần tử của mảng

ten_bien = newKieu_du_lieu[kich_thuoc_mang];

Ví dụ: khởi tạo mảng có 5 phần tử, chưa được gán giá trị cụ thể

arr = newint[5];

Khai báo một mảng, và chỉ rõ số phần tử, các phần tử chưa được gán giá trị cụ thể:

Kieu_du_lieu[] ten_bien = newKieu_du_lieu[kich_thuoc_mang];

Ví dụ: int[] arr = new int[5];

Khai báo một mảng với các phần tử được gán giá trị cụ thể:

Kieu_du_lieu[] ten_bien = {
   giatri0, 
   giatri1,
   ...,
   giatriN
};

Ví dụ:

int[] arr = {
   11,
   12,
   13,
   14,
   15
};

Ví dụ sử dụng mảng

public class ArraySample1 {
   
  public static void main(String[] args) {
 
      // Khai báo một mảng 5 phần tử
      int[] arr = new int[5];
 
      // Chú ý: phần tử đầu tiên của mảng có chỉ số là 0:
      // Gán giá trị cho phần tử đầu tiên (Chỉ số 0)
      arr[0] = 11;
 
      // Gán giá trị cho phần tử thứ hai (Chỉ số 1)
      arr[1] = 12;
 
      arr[2] = 13;
      arr[3] = 14;
      arr[4] = 15;
 
      // In ra màn hình Console số phần tử của mảng.
      System.out.println("Số lượng phần tử = " + arr.length);
 
      // Kiểm tra nếu số lượng phần từ của mảng >= 2 thì hiển thị giá trị của phần tử thứ 2
      if (arr.length >= 2) {
        // In ra phần tử tại chỉ số 1 (Phần tử thứ 2 trong mảng)
        System.out.println(" arr[1]=" + arr[1]);
      }
 
      // Sử dụng vòng lặp for để in ra các phần tử trong mảng.
      System.out.println("Sử dụng for");
      for (int index = 0; index < arr.length; index++) {
          System.out.println("Element " + index + " = " + arr[index]);
      }
 
      // Sử dụng vòng lặp foreach để in ra các phần tử trong mảng.
      System.out.println("Sử dụng foreach");
      int index = 0;
      for (int value : arr) {
          System.out.println("Element " + index + " = " + value);
          index++;
      }
  }
 
}

Kết quả:

Số lượng phần từ = 5
 arr[1]=12
Sử dụng for
Element 0 = 11
Element 1 = 12
Element 2 = 13
Element 3 = 14
Element 4 = 15
Sử dụng foreach
Element 0 = 11
Element 1 = 12
Element 2 = 13
Element 3 = 14
Element 4 = 15

Lưu ý: nên kiểm tra length trước khi sử dụng để tránh lỗi truy cập ngoài chỉ số mảng ArrayIndexOfBoundException

Như ví dụ trên, nếu ta truy cập phần tử arr[5] sẽ bị lỗi ArrayIndexOfBoundException

Ví dụ sử dụng vòng lặp for để khởi tạo giá trị

public class ArraySample2 {
   
  public static void main(String[] args) {
 
      // Khai báo một mảng 5 phần tử
      int[] arr = new int[5];
 
      // Sử dụng for để khởi tạo giá trị trong mảng
      for (int i = 0; i < 5; i++) {
          arr[i] = 11 + i;
      }
       
      // Duyệt qua các phần từ trong mảng sử dụng while
      int index = 0;
      int numberOfElement = arr.length;
      while (index < numberOfElement) {
          System.out.print(arr[index] + " ");
          index++;
      }
  }
 
}

Kết quả:

11 12 13 14 15

Sao chép một mảng trong Java

Bạn có thể sao chép một mảng này sang mảng khác bởi phương thức arraycopy của lớp System.

Cú pháp:

public class ArrayCopySample {
    public static void main(String[] args) {
        char[] copyFrom = { 
            'w', 'e', 'c', 'o', 'm', 'e',
            't', 'o',
            'g', 'p', 'c', 'o', 'd', 'e', 'r'
        };
        char[] copyTo = new char[7];
 
        System.arraycopy(copyFrom, 8, copyTo, 0, 7);
        System.out.println(new String(copyTo));
    }
}

Ví dụ:

public class ArrayCopySample {
    public static void main(String[] args) {
        char[] copyFrom = { 
            'w', 'e', 'c', 'o', 'm', 'e',
            't', 'o',
            'g', 'p', 'c', 'o', 'd', 'e', 'r'
        };
        char[] copyTo = new char[7];
 
        System.arraycopy(copyFrom, 8, copyTo, 0, 7);
        System.out.println(new String(copyTo));
    }
}

Kết quả:

gpcoder

Các phương thức tiện ích cho mảng 1 chiều (Arrays)

Java cung cấp cho bạn một số phương thức tĩnh tiện ích dành cho mảng, chẳng hạn: sắp xếp mảng, gán các giá trị cho toàn bộ các phần tử của mảng, tìm kiếm, so sánh mảng… Các phương thức này được định nghĩa trong lớp Arrays.

// Chuyển một mảng có kiểu T sang danh sách (List) có kiểu T
// T là kiểu đối tượng (Object)
public static <T> List<T> asList(T... a)
   
// Tìm kiếm chỉ số của một giá trị xuất hiện trong mảng.
// (Sử dụng thuật toán tìm kiếm nhị phân (binary search))
public static int binarySearch(type[] a, type key)
 
// Sắp xếp các giá trị của mảng tăng dần
public static void sort(type[] a)
  
// Copy các phần tử của một mảng để tạo ra một mảng mới với độ dài chỉ định.
public static int[] copyOf(type[] original, type newLength)
  
// Copy một phạm vi chỉ định các phần tử của mảng để tạo một mảng mới
public static double[] copyOfRange(type[] original, int from, int to)
  
// So sánh hai mảng
public static boolean equals(type[] a, long[] a2)
  
// Gán cùng một giá trị cho tất cả các phần tử của mảng.
public static void fill(type[] a, type val)
  
// Chuyển một mảng thành chuỗi (string)
public static String toString(type[] a)

Ví dụ:

import java.util.Arrays;
import java.util.List;
 
public class ArraySample3 {
   
  public static void main(String[] args) {
 
      // Khai báo một mảng 5 phần tử kiểu nguyên thủy (primitive)
      int[] arr = {
          15, 5, 10, 30, 25
      };
      System.out.println("Gọi hàm toString để hiển thị mảng: ");
      // Chuyển một mảng thành chuỗi (string)
      System.out.println(Arrays.toString(arr));
       
      // Sắp xếp các giá trị của mảng tăng dần
      Arrays.sort(arr);
       
      System.out.println("Sau khi gọi hàm sort: ");
      // Chuyển một mảng thành chuỗi (string)
      System.out.println(Arrays.toString(arr));
       
      // Tìm kiếm chỉ số của một giá trị xuất hiện trong mảng.
      // Mảng phải được sắp xếp trước khi gọi hàm binarySearch
      int index = Arrays.binarySearch(arr, 30);
      if (index < 0) {
          System.out.println("Không tìm thấy giá trị");
      } else {
          System.out.println("Tìm thấy giá trị 30 tại index  " + index);
      }
 
      // Gán cùng một giá trị cho tất cả các phần tử của mảng.
      Arrays.fill(arr, 11);
       
      System.out.println("Sau khi gọi hàm fill: ");
      // Chuyển một mảng thành chuỗi (string)
      System.out.println(Arrays.toString(arr));
       
 
      // Khai báo một mảng 5 phần tử kiểu đối tượng (object)
      Integer[] arr2 = {
          15, 5, 10, 30, 25
      };
       
      // Chuyển một mảng có kiểu T sang danh sách (List) có kiểu T
      // List: chỉ sử dụng được với object, không sử dụng kiểu nguyên thủy (primitive)
      List<Integer> list = Arrays.asList(arr2);
       
      System.out.println("Sau khi gọi hàm asList: ");
      // Chuyển một mảng thành chuỗi (string)
      System.out.println(list);
  }
 
}

Kết quả:

Gọi hàm toString để hiển thị mảng: 
[15, 5, 10, 30, 25]
Sau khi gọi hàm sort: 
[5, 10, 15, 25, 30]
Tìm thấy giá trị 30 tại index  4
Sau khi gọi hàm fill: 
[11, 11, 11, 11, 11]
Sau khi gọi hàm asList: 
[15, 5, 10, 30, 25]

Mảng hai chiều

Khai báo mảng 2 chiều

Có 3 cách để khai báo một mảng 2 chiều:

// Khai báo một mảng có 5 dòng, 10 cột
Kieu_du_lieu[][] ten_bien_1 = new Kieu_du_lieu[5][10]; 
  
// Khai báo một mảng 2 chiều có 5 dòng.
// (Mảng của mảng)
Kieu_du_lieu[][] ten_bien_2 = new Kieu_du_lieu[5][]; 
  
// Khai báo một mảng 2 chiều, chỉ định giá trị các phần tử. 
Kieu_du_lieu[][] ten_bien_3 = new Kieu_du_lieu[][] {
  
    { value00, value01, value02 }, 
    { value10, value11, value12 } 
};

 Nếu bạn khai báo một mảng của kiểu nguyên thủy ( byte, char, double, float, long, int, short, boolean), các phần tử không được chỉ định giá trị, chúng sẽ có giá trị mặc định

  • Giá trị mặc định 0 ứng với các kiểu byte, double, float, long, int, short.
  • Giá trị mặc định false ứng với kiểu boolean.
  • Giá trị mặc định ‘\u0000’ (Ký tự null) ứng với kiểu char.

Ngược lại, nếu bạn khai báo một mảng của kiểu tham chiếu, nếu một phần tử của mảng không được chỉ định giá trị, nó sẽ có giá trị mặc định là null.

Ví dụ sử dụng mảng hai chiều

public class TwoDimensionalSample1 {
      
    public static void main(String[] args) {
  
        // Khai báo một mảng 2 chiều với 2 dòng, 3 cột
        // Các phần tử có giá trị mặc định
        int[][] arr = new int[2][3];
          
        // Số dòng của mảng 2 chiều.
        System.out.println("Số dòng của mảng 2 chiều: "+ arr.length); // = 2
         
        System.out.println("Hiển thị mảng rỗng"); 
        for (int row = 0; row < arr.length; row++) {
            for (int col = 0; col < arr[row].length; col++) {
                System.out.println("arr[" + row + "," + col + "]=" + arr[row][col]);
            }
        }
         
        System.out.println();
        System.out.println("Khỏi tạo giá trị mảng");  
        // Gán các giá trị cho các phần tử
        for (int row = 0; row < arr.length; row++) {
            for (int col = 0; col < arr[row].length; col++) {
                arr[row][col] = row + col;
            }
        }
 
        System.out.println();
        System.out.println("Hiển thị giá trị sau khi khởi tạo");  
        for (int row = 0; row < arr.length; row++) {
            for (int col = 0; col < arr[row].length; col++) {
                System.out.println("arr[" + row + "," + col + "]=" + arr[row][col]);
            }
        }
  
    }
  
}

Kết quả:

Số dòng của mảng 2 chiều: 2
Hiển thị mảng rỗng
arr[0,0]=0
arr[0,1]=0
arr[0,2]=0
arr[1,0]=0
arr[1,1]=0
arr[1,2]=0
 
Khỏi tạo giá trị mảng
 
Hiển thị giá trị sau khi khởi tạo
arr[0,0]=0
arr[0,1]=1
arr[0,2]=2
arr[1,0]=1
arr[1,1]=2
arr[1,2]=3

Trong Java mảng 2 chiều thực sự là một mảng của mảng, vì vậy bạn có thể khai báo một mảng 2 chiều chỉ cần chỉ định số dòng, không cần phải chỉ rõ số cột. Chính vì mảng 2 chiều là “Mảng của mảng” nên thuộc tính length của mảng 2 chiều trả về số dòng của mảng.

Ví dụ:

public class TwoDimensionalSample1 {
 
    public static void main(String[] args) {
 
        // Khai báo một mảng 2 chiều (Mảng của mảng)
        // Khởi tạo mảng có 2 dòng
        int[][] arr = new int[2][];
 
        // Khởi tạo giá trị cho phần từ thứ 2
        arr[0] = new int[] { 
                1, 2
        };
 
        // Khởi tạo giá trị cho phần tử thứ nhất
        arr[1] = new int[] { 1, 2, 3, 4 };
 
        for (int row = 0; row < arr.length; row++) {
            for (int col = 0; col < arr[row].length; col++) {
                System.out.println("Giá trị tại [" + row + "][" + col + "]=" + arr[row][col]);
            }
        }
    }
 
}

Kết quả

Giá trị tại [0][0]=1
Giá trị tại [0][1]=2
Giá trị tại [1][0]=1
Giá trị tại [1][1]=2
Giá trị tại [1][2]=3
Giá trị tại [1][3]=4

Lợi thế và hạn chế của mảng trong Java

Lợi thế

  • Cho phép xử lí nhiều thành phần dữ liệu trong cùng 1 thời điểm.
  • Bộ nhớ chỉ được cấp cho mảng khi mảng thực sự được sử dụng.
  • Truy cập ngẫu nhiên: chúng ta có thể lấy bất cứ dữ liệu nào ở tại bất cứ vị trí chỉ mục nào.

Hạn chế

  • Giới hạn kích cỡ: Kích thước mảng cố định không thay đổi. Nó không tăng kích thước của nó tại runtime. Để xử lý vấn đề này, Collection Framework được sử dụng trong Java.
    • Các phần tử của một mảng là được đặt và tham chiếu liên tiếp nhau trong bộ nhớ, điều đó là khó khăn khi ta cố tình bỏ đi một phần tử nào đó trong mảng, nó mất tính liên tiếp. Thông thường một kỹ thuật mà thường sử dụng là tạo một mảng mới lưu trữ các đối tượng của mảng ban đầu và bỏ đi các phần tử không cần thiết, tuy nhiên điều này làm giảm hiệu năng của chương trình.
    • Với trường hợp mở rộng mảng cũng với kỹ thuật tương tự là khởi tạo một mảng mới với kích cỡ lớn hơn sau đó thì copy các phần tử mảng cũ cho mảng mới .
  • Chỉ lưu trữ được duy nhất cùng một kiểu dữ liệu trong mảng