3.9陣列
3.9.1陣列基本使用
陣列,英文叫Array,是一種資料結構,是用來存放同一資料型別數值的集合。例如存放30個int型數值、存放100個double型數值等等。
我們知道使用一個變數,需要先宣告一個變數,例如:int a;使用陣列同樣也需要先宣告一個陣列變數。假設我們要宣告一個int型別的陣列變數,有2種方式:
int[] a; int b[];
但是一般Java工作者都習慣於第一種方式,因為int[]看起來更像資料型別,後面跟一個變數名。
宣告變數,實際上是在記憶體中給它分配一塊空間。但是陣列是存放若干個資料,因此還得繼續宣告它的大小,即存放多少個資料。Java中使用new運算子來操作。像下面這樣:
a = new int[30];
我們還可以在宣告陣列的同時就分配空間:
int[] a = new int[30];
上面這條語句宣告int型的陣列a可以存放30個int的數值。這樣,就會在記憶體中分配30個連續的空間。
陣列大小分配好了以後。我們要訪問陣列中的某一個元素的話,可以用一個整型的下標(index)來訪問。下標是從0開始的,因此上面的陣列a的下標是0~29。比如我們要訪問第29個元素,那麼可以用a[28]。這裡需要注意,陣列一旦被建立了以後,大小就是固定的。如果下標超出範圍,例如訪問a[30],程式會報異常,異常一般是:“array index out of bounds”,稱做“下標越界”。
給陣列的元素賦值就很簡單了,就像給一個普通變數賦值一樣:
a[22] = 22;
我們還可以在宣告陣列的時候同時賦值,有兩種形式:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = { 1, 2, 3, 4 };
注意第一種形式,[]內不需要指定大小。有的時候,我們還需要知道陣列的大小,可以用a.length來獲得。例如我們想遍歷列印陣列的值:
for (int i = 0; i < a.length; i++) { System.out.println(a[i]); }
綜合上面的討論,我們可以歸納一下陣列的3要素:
- 宣告一個陣列,有2種形式,一般採用 “資料型別[] 變數名” 的形式
- 給陣列分配大小,用new關鍵字,形式為 “變數名=new 資料型別[大小]”。一旦分配完大小,陣列的大小就固定了,可以用“變數名.length”來獲取陣列的大小。訪問陣列的元素用“變數名[下標]”的方式。下標的範圍是0~length。如果不在這個範圍內,程式會報“下標越界”異常。
- 給陣列的元素賦值
訪問陣列的元素用“變數名[下標]”的方式。下標的範圍是0~length。如果不在這個範圍內,程式會報“下標越界”異常。
用一張圖總結一下:
3.9.2陣列的迴圈
在實際運用中,經常會有遍歷陣列的需求。上面我們用for演示過遍歷陣列的情況。事實上,在Java5.0之後,有另外一種for迴圈的結構,可以非常方便的遍歷一個集合中的元素。程式碼形式為:
for(型別 變數:集合){語句}
我們看一個例子:
int[] a = new int[] { 1, 2, 3, 4 }; for (int i : a) { System.out.println(i); }
執行結果:
1 2 3 4
這種for迴圈可以理解為“遍歷集合中的每一個元素”。
3.9.3陣列拷貝
在實際工作中,還會經常碰到需要將一個陣列中的全部或部分元素拷貝到另一個元素中的需求。如果是全量拷貝,有一個很簡單的辦法:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = a;
執行以上程式碼後,陣列b和陣列a就一樣了。但是這樣有一個問題,我們繼續編寫程式碼:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = a; b[3] = 33;// 將陣列的b的第4個元素賦值為33 System.out.println(a[3]);// 結果陣列a的第4個元素也變成33
我們修改陣列b的第4個元素,結果陣列a的第4個元素也跟著一塊修改了。這是為什麼呢?這是因為Java中變數的的賦值,是引用賦值,用記憶體的表現來解釋可以一目瞭然:
把變數a賦值給變數b,實際上b和a將指向同一個記憶體地址。因此修改b的元素,實際上就是修改記憶體中的值,這樣a的元素自然也就跟著修改了。我們稱這種拷貝為“淺拷貝”。如果想要實現另外分配一塊記憶體空間給陣列b,有沒有辦法呢?Java給我們提供了2種方法,一種是用System類的arraycopy方法,還有一種是Java6之後新提供的Arrays類的copyOf方法。具體怎麼用?還記得安裝JDK的時候,提到過的API文件嗎?我們這時候就需要用到它了。(如果不記得了,回到那一節看一遍)。
筆者的API文件的路徑是:D:\Java大失叔\Java\jdk-8u261-docs-all\docs,我們找到api目錄下的index.html,用瀏覽器開啟,可以看到首頁:
左上是所有的包,左下是當前包下的類,右邊是當前類的API說明。將來我們會經常用到API文件來查詢類的使用說明。我們先來看一下System的arraycopy方法。
System類在java.lang包下,我們定位到System類後,找到arraycopy方法,點選方法名,可以進入該方法的詳細說明。我們摘抄方法體:
arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
這個方法的作用就是從源陣列src的srcPos下標開始,拷貝length個元素到目標陣列dest中,目標陣列的起始下標為destPos。我們直接上程式碼:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = new int[4]; System.arraycopy(a, 0, b, 0, 4); b[3] = 33;// 將陣列的b的第4個元素賦值為33 System.out.println(b[3]);// 陣列b的第4個元素變成33 System.out.println(a[3]);// 陣列a的第4個元素仍然是4
接下來,我們看一下Arrays.copyOf方法,Arrays類在java.util包下,util包提供了很多有用的工具類,Arrays是其中之一,我們會發現有很多copyOf方法,我們找到對應int型的:
copyOf(int[] original, int newLength)
這個方法的作用就是將源陣列original的所有元素拷貝到一個新的陣列中,可以指定新的陣列的大小newLength,然後返回新的陣列。如果newLength比源陣列大小大,那麼新陣列多餘的元素將被賦值為0,如果newLength比源陣列大小小,那麼將只拷貝newLength個元素。我們上程式碼:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = Arrays.copyOf(a, 5); int[] c = Arrays.copyOf(a, 3); b[0] = 33;// 將陣列的b的第4個元素賦值為33 c[0] = 44;// 將陣列的b的第4個元素賦值為33 System.out.println(a[0]);// 陣列a的第1個元素仍然是4 System.out.println(b[0]);// 陣列b的第1個元素變成33 System.out.println(c[0]);// 陣列c的第1個元素變成44
3.9.4陣列排序
陣列的排序也可以用Arrays類的sort方法,我們摘抄方法:
sort(int[] a)
這個方法對陣列a進行升序排序。它內部採用的是優化的快速排序演算法,這個演算法對於大多數的資料集合來說效率都比較高。我們看一下程式碼:
int[] a = new int[] { 1, 4, 2, 3 }; Arrays.sort(a); for (int i : a) { System.out.println(i); }
執行結果:
1 2 3 4
排序之後,按照升序排列了。
Arrays類還有很多有用的方法,這裡就不一一列舉了,大家以後如果碰到需要對陣列進行某些操作的時候,可以想到來查一下Arrays類,看看有沒有對應的方法。
3.9.5多維陣列
Java中還支援多維陣列,但是其實在實際運用中很少用到,最多也就用一下二維陣列,因此這裡只粗略的介紹一下二維陣列。我們經常用到Excel表格,其實就可以看成一個二維陣列,例如:
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
31 |
32 |
33 |
34 |
41 |
42 |
43 |
44 |
51 |
52 |
53 |
54 |
宣告二維陣列、分配空間和賦值訪問和一維陣列類似,我們用程式碼演示:
int[][] table;// 宣告一個二維陣列 table = new int[5][4];// 分配空間5行4列 table[0][1] = 12;// 第1行第2列賦值為12
對於賦值,也可以和一維陣列一樣,在宣告的同時就賦值:
int[][] table = new int[][] { { 11, 12, 13, 14 }, { 21, 22, 23, 24 } };// 宣告一個二維陣列,並賦值 int[][] table2 = { { 11, 12, 13, 14 }, { 21, 22, 23, 24 } };// 宣告一個二維陣列,並賦值
二維陣列其實可以看成是一個一維陣列,然後該維度陣列的每一個元素又是一個一維陣列。用圖可以表示如下:
因此,聰明的你可能發現了,Java的二維陣列中,陣列的length的值是第一維度的大小。並且我們在分配二維陣列大小的時候,可以只分配第一維度的大小,然後再給第一維度的陣列的每一個元素分配不同的大小,例如:
int[][] table = new int[4][];// 只分配第一位維度的大小為4 table[0] = new int[1];// 給table[0]分配大小為1 table[0][0] = 11; table[1] = new int[] { 21, 22 };// 給table[1]分配大小為2,同時賦值 table[2] = new int[] { 31, 32, 33 };// 給table[2]分配大小為3,同時賦值 table[3] = new int[] { 41, 42, 43, 44 };// 給table[3]分配大小為4,同時賦值
這其實是一個不規則的二維陣列。用表格表示如下圖:
11 |
|
|
|
21 |
22 |
|
|
31 |
32 |
33 |
|
41 |
42 |
43 |
44 |