Java數組(Array)最全匯總(中篇)

前言

本章是關於Java數組的最全匯總,本篇為匯總中篇,主要講瞭二維數組和不規則的數組的相關內容。

數組是最常見的一種數據結構,它是相同類型的用一個標識符封裝到一起的基本類型數據序列或者對象序列。

數組使用一個統一的數組名和不同的下標來唯一確定數組中的元素。

實質上,數組是一個簡單的線性序列,因此訪問速度很快。

本章將詳細介紹 Java 中數組的創建、初始化和處理方法,如獲取數組長度、查找數組元素和數組排序等。

本章學習要點

  1. 掌握一維數組的創建方法
  2. 掌握一維數組的初始化方法
  3. 熟練掌握一維數組的應用
  4. 掌握二維數組的創建
  5. 掌握二維數組的初始化
  6. 熟練掌握二維數組元素的訪問方式
  7. 掌握數組的復制方法
  8. 掌握搜索數組元素的方法
  9. 掌握對數組的排序算法

Java二維數組詳解

為瞭方便組織各種信息,計算機常將信息以表的形式進行組織,然後再以行和列的形式呈現出來。

二維數組的結構決定瞭其能非常方便地表示計算機中的表,以第一個下標表示元素所在的行,第二個下標表示元素所在的列。

下面簡單瞭解一下二維數組,包括數組的聲明和初始化。

創建二維數組

在 Java 中二維數組被看作數組的數組,即二維數組為一個特殊的一維數組,其每個元素又是一個一維數組。

Java 並不直接支持二維數組,但是允許定義數組元素是一維數組的一維數組,以達到同樣的效果。聲明二維數組的語法如下:

type arrayName[][]; // 數據類型 數組名[][];

type[][] arrayName; // 數據類型[][] 數組名;

其中,type 表示二維數組的類型,arrayName 表示數組名稱,第一個中括號表示行,第二個中括號表示列。

下面分別聲明 int 類型和 char 類型的數組,代碼如下:

int[][] age;char[][] sex;

初始化二維數組

二維數組可以初始化,和一維數組一樣,可以通過 3 種方式來指定元素的初始值。這 3 種方式的語法如下:

type[][] arrayName = new type[][]{值 1,值 2,值 3,…,值 n};    // 在定義時初始化
type[][] arrayName = new type[size1][size2];    // 給定空間,在賦值
type[][] arrayName = new type[size][];    // 數組第二維長度為空,可變化

例 1

使用第一種方式聲明 int 類型的二維數組,然後初始化該二維數組。代碼如下:

int[][] temp = new int[][]{{1,2},{3,4}};

上述代碼創建瞭一個二行二列的二維數組 temp,並對數組中的元素進行瞭初始化。圖 1 所示為該數組的內存結構。

img

圖1 二維數組內存結構

使用第二種方式聲明 int 類型的二維數組,然後初始化該二維數組。代碼如下:

int[][] temp = new int[2][2];

使用第三種方式聲明 int 類型的二維數組,並且初始化數組。代碼如下:

int[][] temp = new int[2][];

獲取單個元素

在上部分使用的前 2 種方式創建並初始化瞭一個二行二列的 int 類型數組 temp。

當需要獲取二維數組中元素的值時,也可以使用下標來表示。語法如下:

arrayName[i-1][j-1];

其中,arrayName 表示數組名稱,i 表示數組的行數,j 表示數組的列數。

例如,要獲取第二行第二列元素的值,應該使用 temp[1][1]來表示。

這是由於數組的下標起始值為 0,因此行和列的下標需要減 1。

例 2

通過下標獲取 class_score 數組中第二行第二列元素的值與第四行第一列元素的值。代碼如下:

public static void main(String[] args) {
    double[][] class_score = {{10.0,99,99},{100,98,97},{100,100,99.5},{99.5,99,98.5}};
    System.out.println("第二行第二列元素的值:"+class_score[1][1]);
    System.out.println("第四行第一列元素的值:"+class_score[3][0]);
}

執行上述代碼,輸出結果如下:

第二行第二列元素的值:98.0
第四行第一列元素的值:99.5

獲取全部元素

在一維數組中直接使用數組的 length 屬性獲取數組元素的個數。

而在二維數組中,直接使用 length 屬性獲取的是數組的行數,在指定的索引後加上 length(如 array[0].length)表示的是該行擁有多少個元素,即列數。

如果要獲取二維數組中的全部元素,最簡單、最常用的辦法就是使用 for 語句。

在一維數組全部輸出時,我們使用一層 for 循環,而二維數組要想全部輸出,則使用嵌套 for 循環(2 層 for 循環)。

例 3

使用 for 循環語句遍歷 double 類型的 class_score 數組的元素,並輸出每一行每一列元素的值。代碼如下:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    for (int i = 0; i < class_score.length; i++) { // 遍歷行
        for (int j = 0; j < class_score[i].length; j++) {
            System.out.println("class_score[" + i + "][" + j + "]=" + class_score[i][j]);
        }
    }
}

上述代碼使用嵌套 for 循環語句輸出二維數組。

在輸出二維數組時,第一個 for 循環語句表示以行進行循環,第二個 for 循環語句表示以列進行循環,這樣就實現瞭獲取二維數組中每個元素的值的功能。

執行上述代碼,輸出結果如下所示。

class_score[0][0]=100.0
class_score[0][1]=99.0
class_score[0][2]=99.0
class_score[1][0]=100.0
class_score[1][1]=98.0
class_score[1][2]=97.0
class_score[2][0]=100.0
class_score[2][1]=100.0
class_score[2][2]=99.5
class_score[3][0]=99.5
class_score[3][1]=99.0
class_score[3][2]=98.5

例 4

假設有一個矩陣為 5 行 5 列,該矩陣是由程序隨機產生的 10 以內數字排列而成。下面使用二維數組來創建該矩陣,代碼如下:

public class Test11 {
    public static void main(String[] args) {
        // 創建一個二維矩陣
        int[][] matrix = new int[5][5];
        // 隨機分配值
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = (int) (Math.random() * 10);
            }
        }
        System.out.println("下面是程序生成的矩陣\n");
        // 遍歷二維矩陣並輸出
        for (int k = 0; k < matrix.length; k++) {
            for (int g = 0; g < matrix[k].length; g++) {
                System.out.print(matrix[k][g] + "");
            }
            System.out.println();
        }
    }
}

在該程序中,首先定義瞭一個二維數組,然後使用兩個嵌套的 for 循環向二維數組中的每個元素賦值。

其中,Math.random() 方法返回的是一個 double 類型的數值,數值為 0.6、0.9 等,因此乘以 10 之後為 10 以內的整數。

最後又使用瞭兩個嵌套的 for 循環遍歷二維數組,輸出二維數組中的值,從而產生矩陣。

運行該程序的結果如下所示。

下面是程序生成的矩陣

78148
69230
43823
75663
05688

for each 循環語句不能自動處理二維數組的每一個元素。

它是按照行, 也就是一維數組處理的。要想訪問二維教組 a 的所有元素, 需要使用兩個嵌套的循環, 如下所示:

for (double[] row : a) {
    for (double value : row) {
        ......
    }
}

把【例2】修改為使用 for each 循環語句輸出,代碼如下所示:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    for (double[] row : class_score) {
        for (double value : row) {
            System.out.println(value);
        }
    }
}

輸出結果為:

100.0
99.0
99.0
100.0
98.0
97.0
100.0
100.0
99.5
99.5
99.0
98.5

提示:要想快速地打印一個二維數組的數據元素列表,可以調用:

System.out.println(Arrays.deepToString(arrayName));

代碼如下:

System.out.println(Arrays.deepToString(class_score));

輸出格式為:

[[100.0, 99.0, 99.0], [100.0, 98.0, 97.0], [100.0, 100.0, 99.5], [99.5, 99.0, 98.5]]

獲取整行元素

除瞭獲取單個元素和全部元素之外,還可以單獨獲取二維數組的某一行中所有元素的值,或者二維數組中某一列元素的值。

獲取指定行的元素時,需要將行數固定,然後隻遍歷該行中的全部列即可。

例 5

編寫一個案例,接收用戶在控制臺輸入的行數,然後獲取該行中所有元素的值。代碼如下:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    Scanner scan = new Scanner(System.in);
    System.out.println("當前數組隻有" + class_score.length + "行,您想查看第幾行的元素?請輸入:");
    int number = scan.nextInt();
    for (int j = 0; j < class_score[number - 1].length; j++) {
        System.out.println("第" + number + "行的第[" + j + "]個元素的值是:" + class_score[number - 1][j]);
    }
}

執行上述代碼進行測試,輸出結果如下所示。

當前數組隻有4行,您想查看第幾行的元素?請輸入:
3
第3行的第[0]個元素的值是:100.0
第3行的第[1]個元素的值是:100.0
第3行的第[2]個元素的值是:99.5

獲取整列元素

獲取指定列的元素與獲取指定行的元素相似,保持列不變,遍歷每一行的該列即可。

例 6

編寫一個案例,接收用戶在控制臺中輸入的列數,然後獲取二維數組中所有行中該列的值。代碼如下:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    Scanner scan = new Scanner(System.in);
    System.out.println("您要獲取哪一列的值?請輸入:");
    int number = scan.nextInt();
    for (int i = 0; i < class_score.length; i++) {
        System.out.println("第 " + (i + 1) + " 行的第[" + number + "]個元素的值是" + class_score[i][number]);
    }
}

執行上述代碼進行測試,如下所示。

您要獲取哪一列的值?請輸入:
2
第 1 行的第[2]個元素的值是99.0
第 2 行的第[2]個元素的值是97.0
第 3 行的第[2]個元素的值是99.5
第 4 行的第[2]個元素的值是98.5

Java不規則數組

通過前面的學習我們知道 Java 實際上沒有多維數組,隻有一維數組。

多維數組被解釋為是數組的數組,所以因此會衍生出一種不規則數組。

規則的 4×3 二維數組有 12 個元素,而不規則數組就不一定瞭。

如下代碼靜態初始化瞭一個不規則數組。

int intArray[][] = {{1,2}, {11}, {21,22,23}, {31,32,33}};

高維數組(二維以及二維以上的數組稱為高維數組)是 4 個元素,但是低維數組元素個數不同,如圖 1 所示,其中第 1 個數組有兩個元素,第 2 個數組有 1 個元素,第 3 個數組有 3 個元素,第 4 個數組有 3 個元素。這就是不規則數組。

不規則數組

圖 1 不規則數組

動態初始化不規則數組比較麻煩,不能使用 new int[4][3] 語句,而是先初始化高維數組,然後再分別逐個初始化低維數組。代碼如下:

int intArray[][] = new int[4][]; //先初始化高維數組為4
// 逐一初始化低維數組
intArray[0] = new int[2];
intArray[1] = new int[1];
intArray[2] = new int[3];
intArray[3] = new int[3];

從上述代碼初始化數組完成之後,不是有 12 個元素而是 9 個元素,它們的下標索引如圖 2 所示,可見其中下標 [0][2]、[1][1] 和 [1][2] 是不存在的,如果試圖訪問它們則會拋出下標越界異常。

不規則數組訪問

圖 2 不規則數組訪問

提示:下標越界異常(ArrayIndexOutOfBoundsException)是試圖訪問不存在的下標時引發的。

例如一個一維 array 數組如果有 10 個元素,那麼表達式 array[10] 就會發生下標越界異常,這是因為數組下標是從 0 開始的,最後一個元素下標是數組長度減 1,所以 array[10] 訪問的元素是不存在的。

下面介紹一個不規則數組的示例:

import java.util.Arrays;
public class HelloWorld {
    public static void main(String[] args) {
        int intArray[][] = new int[4][]; // 先初始化高維數組為4
        // 逐一初始化低維數組
        intArray[0] = new int[2];
        intArray[1] = new int[1];
        intArray[2] = new int[3];
        intArray[3] = new int[3];
        // for循環遍歷
        for (int i = 0; i < intArray.length; i++) {
            for (int j = 0; j < intArray[i].length; j++) {
                intArray[i][j] = i + j;
            }
        }
        // for-each循環遍歷
        for (int[] row : intArray) {
            for (int column : row) {
                System.out.print(column);
                // 在元素之間添加制表符,
                System.out.print('\t');
            }
            // 一行元素打印完成後換行
            System.out.println();
        }
        System.out.println(intArray[0][2]); // 發生運行期錯誤
    }
}

不規則數組訪問和遍歷可以使用 for 和 for-each 循環,但要註意下標越界異常發生。

上述代碼第 18 行和第 19 行采用 for-each 循環遍歷不規則數組,其中代碼第 18 行 for-each 循環取出的數據是 int 數組,所以 row 類型是 int[]。代碼第 19 行 for-each 循環取出的數據是 int 數據,所以 column 的類型 int。

另外,註意代碼第 27 行試圖訪問 intArray[0][2]元素,由於 [0][2] 不存在所以會發生下標越界異常。

Java數組也是一種數據類型

Java 的數組要求所有的數組元素具有相同的數據類型。

因此,在一個數組中,數組元素的類型是唯一的,即一個數組裡隻能存儲一種數據類型的數據,而不能存儲多種數據類型的數據。

因為 Java 語言是面向對象的語言,而類與類之間可以支持繼承關系(從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行為),這樣可能產生一個數組裡可以存放多種數據類型的假象。

例如有一個水果數組,要求每個數組元素都是水果,實際上數組元素既可以是蘋果,也可以是香蕉(蘋果、香蕉都繼承瞭水果,都是一種特殊的水果),但這個數組的數組元素的類型還是唯一的,隻能是水果類型。

一旦數組的初始化完成,數組在內存中所占的空間將被固定下來,因此數組的長度將不可改變。

即使把某個數組元素的數據清空,但它所占的空間依然被保留,依然屬於該數組,數組的長度依然不變。

Java 的數組既可以存儲基本類型的數據,也可以存儲引用類型的數據,隻要所有的數組元素具有相同的類型即可。

值得指出的是,數組也是一種數據類型,它本身是一種引用類型。

例如 int 是一個基本類型,但 int[](這是定義數組的一種方式)就是一種引用類型瞭。

int[] 是一種類型嗎?怎麼使用這種類型呢?

沒錯,int[] 就是一種數據類型,與 int 類型、String 類型相似,一樣可以使用該類型來定義變量,也可以使用該類型進行類型轉換等。

使用 int[] 類型來定義變量、進行類型轉換時與使用其他普通類型沒有任何區別。

int[] 類型是一種引用類型,創建 int[] 類型的對象也就是創建數組,需要使用創建數組的語法。

Java中到底有沒有多維數組(長篇神文)?

Java 中沒有多維數組的概念,從數組底層的運行機制上來看 Java 沒有多維數組,但是 Java 提供瞭支持多維數組的語法,可以實現多維數組的功能。

Java 語言裡的數組類型是引用類型,因此數組變量其實是一個引用,這個引用指向真實的數組內存。數組元素的類型也可以是引用,如果數組元素的引用再次指向真實的數組內存,這種情形看上去很像多維數組。

定義數組類型的語法

type[] arrName;

是典型的一維數組的定義語法,其中 type 是數組元素的類型。

如果希望數組元素也是一個引用,而且是指向 int 數組的引用,則可以把 type 具體成 int[](前面已經指出,int[] 就是一種類型,int[] 類型的用法與普通類型並無任何區別),那麼上面定義數組的語法就是

int[][] arrName

如果把 int 這個類型擴大到 Java 的所有類型(不包括數組類型),則出現瞭定義二維數組的語法:

type[][] arrName;

Java 語言采用上面的語法格式來定義二維數組,但它的實質還是一維數組,隻是其數組元素也是引用,數組元素裡保存的引用指向一維數組。

接著對這個“二維數組”執行初始化,同樣可以把這個數組當成一維數組來初始化,把這個“二維數組”當成一個一維數組,其元素的類型是 type[] 類型,則可以采用如下語法進行初始化:

arrName = new type[length][]

上面的初始化語法相當於初始化瞭一個一維數組,這一維數組的長度是 length。

同樣,因為這個一維數組的數組元素是引用類型(數組類型)的,所以系統為每個數組元素都分配初始值:null。

這個二維數組實際上完全可以當成一維數組使用:使用

new type[length]

初始化一維數組後,相當於定義瞭 length 個 type 類型的變量。

類似的,使用

new type[length][]

初始化這個數組後,相當於定義瞭 length 個 type[] 類型的變量。

當然,這些 type[] 類型的變量都是數組類型,因此必須再次初始化這些數組。

下面程序示范瞭如何把二維數組當成一維數組處理。

public class TwoDimensionTest {    
    public static void main(String[] args) {        
        // 定義一個二維數組        
        int[][] a;        // 把a當成一維數組進行初始化,初始化a是一個長度為4的數組        
        // a數組的數組元素又是引用類型       
        a = new int[4][];        // 把a數組當成一維數組,遍歷a數組的每個數組元素        
        for (int i = 0, len = a.length; i < len; i++) {            
            System.out.println(a[i]); // 輸出 null null null null        
            }        // 初始化a數組的第一個元素        
            a[0] = new int[2];        
            // 訪問a數組的第一個元素所指數組的第二個元素        
            a[0][1] = 6;        
            // a數組的第一個元素是一個一維數組,遍歷這個一維數組        
            for (int i = 0, len = a[0].length; i < len; i++) {            
                System.out.println(a[0][i]); // 輸出 0 6        
                }    
        }
}

上面程序中粗體字代碼部分把 a 這個二維數組當成一維數組處理,隻是每個數組元素都是 null,所以看到輸出結果都是 null。

下面結合示意圖來說明這個程序的執行過程。

程序中代碼

int[][] a;

將在棧內存中定義一個引用變量,這個變量並未指向任何有效的內存空間,此時的堆內存中還未為這行代碼分配任何存儲區。

程序中代碼

a = new int[4][];

對a 數組執行初始化,這行代碼讓 a 變量指向一塊長度為 4 的數組內存,這個長度為 4 的數組裡每個數組元素都是引用類型(數組類型),系統為這些數組元素分配默認的初始值:null。此時 a 數組在內存中的存儲示意圖如圖 1 所示。

將二維數組當成一維數組初始化的存儲示意圖

圖 1 將二維數組當成一維數組初始化的存儲示意圖

從圖 1 來看,雖然聲明 a 是一個二維數組,但這裡絲毫看不出它是一個二維數組的樣子,完全是一維數組的樣子。

這個一維數組的長度是 4,隻是這 4 個數組元素都是引用類型,它們的默認值是 null。

所以程序中可以把 a 數組當成一維數組處理,依次遍歷 a 數組的每個元素,將看到每個數組元素的值都是 null。

由於 a 數組的元素必須是 int[] 數組,所以接下來的程序對 a[0] 元素執行初始化,也就是讓圖 1 右邊堆內存中的第一個數組元素指向一個有效的數組內存,指向一個長度為 2 的 int 數組。

因為程序采用動態初始化 a[0] 數組,因此系統將為 a[0] 所引用數組的每個元素分配默認的初始值:0,然後程序顯式為 a[0] 數組的第二個元素賦值為 6。

此時在內存中的存儲示意圖如圖 2 所示。

初始化a[0]後的存儲示意圖

圖 2 初始化a[0]後的存儲示意圖

圖 2 中灰色覆蓋的數組元素就是程序顯式指定的數組元素值。

TwoDimensionTest.java 接著迭代輸出 a[0] 數組的每個數組元素,將看到輸出 0 和 6。

是否可以讓圖 2 中灰色覆蓋的數組元素再次指向另一個數組?這樣不就可以擴展成三維數組,甚至擴展成更多維的數組嘛?

不能!至少在這個程序中不能。

因為 Java 是強類型語言,當定義 a 數組時,已經確定瞭 a 數組的數組元素是 int[] 類型,則 a[0] 數組的數組元素隻能是 int 類型,所以灰色覆蓋的數組元素隻能存儲 int 類型的變量。

對於其他弱類型語言,例如 JavaScript 和 Ruby 等,確實可以把一維數組無限擴展,擴展成二維數組、三維數組…,如果想在 Java 語言中實現這種可無限擴展的數組,則可以定義一個 Object[] 類型的數組,這個數組的元素是 Object 類型,因此可以再次指向一個 Object[] 類型的數組,這樣就可以從一維數組擴展到二維數組、三維數組…

從上面程序中可以看出,初始化多維數組時,可以隻指定最左邊維的大小;當然,也可以一次指定每一維的大小。例如下面代碼:

// 同時初始化二維數組的兩個維數
int[][] b = new int[3][4];

上面代碼將定義一個 b 數組變量,這個數組變量指向一個長度為 3 的數組,這個數組的每個數組元素又是一個數組類型,它們各指向對應的長度為 4 的 int[] 數組,每個數組元素的值為 0。

這行代碼執行後在內存中的存儲示意圖如圖 3 所示。

同時初始化二維數組的兩個維數後的存儲示意圖

圖 3 同時初始化二維數組的兩個維數後的存儲示意圖

還可以使用靜態初始化方式來初始化二維數組。

使用靜態初始化方式來初始化二維數組時,二維數組的每個數組元素都是一維數組,因此必須指定多個一維數組作為二維數組的初始化值。如下代碼所示:

// 使用靜態初始化語法來初始化一個二維數組
String[][] str1 = new String[][]{new String[3], new String[]{“hello”}};
// 使用簡化的靜態初始化語法來初始化二維數組
String[][] str2 = {new String[3], new String [] {“hello”}};

上面代碼執行後內存中的存儲示意圖如圖 4 所示。

采用靜態初始化語法初始化二維數組的存儲示意圖

圖 4 采用靜態初始化語法初始化二維數組的存儲示意圖

通過上面講解可以得到一個結論:二維數組是一維數組,其數組元素是一維數組。三維數組也是一維數組,其數組元素是二維數組…… 從這個角度來看,Java 語言裡沒有多維數組。

到此這篇關於Java數組(Array)最全匯總(中篇)的文章就介紹到這瞭,其他兩個部分的內容(上、下篇)請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: