<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本章是關於Java陣列的最全彙總,本篇為彙總中篇,主要講了二維陣列和不規則的陣列的相關內容。
陣列是最常見的一種資料結構,它是相同型別的用一個識別符號封裝到一起的基本型別資料序列或者物件序列。
陣列使用一個統一的陣列名和不同的下標來唯一確定陣列中的元素。
實質上,陣列是一個簡單的線性序列,因此存取速度很快。
本章將詳細介紹 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][]; // 陣列第二維長度為空,可變化
使用第一種方式宣告 int 型別的二維陣列,然後初始化該二維陣列。程式碼如下:
int[][] temp = new int[][]{{1,2},{3,4}};
上述程式碼建立了一個二行二列的二維陣列 temp,並對陣列中的元素進行了初始化。圖 1 所示為該陣列的記憶體結構。
圖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。
通過下標獲取 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 迴圈)。
使用 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
假設有一個矩陣為 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]]
除了獲取單個元素和全部元素之外,還可以單獨獲取二維陣列的某一行中所有元素的值,或者二維陣列中某一列元素的值。
獲取指定行的元素時,需要將行數固定,然後只遍歷該行中的全部列即可。
編寫一個案例,接收使用者在控制檯輸入的行數,然後獲取該行中所有元素的值。程式碼如下:
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
獲取指定列的元素與獲取指定行的元素相似,保持列不變,遍歷每一行的該列即可。
編寫一個案例,接收使用者在控制檯中輸入的列數,然後獲取二維陣列中所有行中該列的值。程式碼如下:
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 實際上沒有多維陣列,只有一維陣列。
多維陣列被解釋為是陣列的陣列,所以因此會衍生出一種不規則陣列。
規則的 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 的陣列既可以儲存基本型別的資料,也可以儲存參照型別的資料,只要所有的陣列元素具有相同的型別即可。
值得指出的是,陣列也是一種資料型別,它本身是一種參照型別。
例如 int 是一個基本型別,但 int[](這是定義陣列的一種方式)就是一種參照型別了。
int[] 是一種型別嗎?怎麼使用這種型別呢?
沒錯,int[] 就是一種資料型別,與 int 型別、String 型別相似,一樣可以使用該型別來定義變數,也可以使用該型別進行型別轉換等。
使用 int[] 型別來定義變數、進行型別轉換時與使用其他普通型別沒有任何區別。
int[] 型別是一種參照型別,建立 int[] 型別的物件也就是建立陣列,需要使用建立陣列的語法。
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 所示。
圖 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)最全彙總(中篇)的文章就介紹到這了,其他兩個部分的內容(上、下篇)請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45