從這一節開始,我們就要正式進去資料結構的世界了,那麼第一個是什麼呢,就是我們的數組。
在我想寫數組的時候,我的第一印象是去看它的源碼,很可惜,數組的實作太特殊了,找了很久,我沒有找到它的源碼,帶着這樣的思考,我就開始了Java中數組的挖掘。Wow,真香!
一、Java中數組的介紹
數組是一種最簡單的複合資料類型,它是有序資料的集合,數組中的每個元素具有相同的資料類型,可以用一個統一的數組名和不同的下标來唯一确定數組中的元素。根據數組的次元,可以将其分為一維數組、二維數組和多元數組等。一定要注意,數組隻能存放同一種資料類型(Object類型數組除外)。
二、數組是一個引用類型嗎?
先給答案,是的,沒有任何疑問。
注意,數組也是一種資料類型,它本身是一種引用類型。
數組是一種大小固定的資料結構,對線性表的所有操作都可以通過數組來實作。雖然數組一旦建立之後,它的大小就無法改變了,但是當數組不能再存儲線性表中的新元素時,我們可以建立一個新的大的數組來替換目前數組。這樣就可以使用數組實作動态的資料結構。
如何驗證?
定義一個數組,發現它擁有Object類的所有方法。

根據這個例子,其實大家已經看出來了,數組擁有超類Object的所有方法,說明他也是一個類。并且他擁有自己的clone()方法和length屬性。
三、如何了解數組的底層實作
既然數組擁有Object的所有方法,那我們是否能檢視一下數組的源碼,來了解一下數組的實作呢?
可惜,數組太特殊了,他的實作是虛拟機編譯的時候動态生成的,是以我們無法直接檢視源碼,隻能通過檢視編譯後的class的位元組碼一探究竟。
JVM 中數組對象是一種特殊的對象,虛拟機從數組的中繼資料中無法确認數組的大小,它的Object Header 比普通對象多了一個word 來存儲數組的長度,length 會編譯成對應的位元組碼讀取這個field 就可以了。
我分别定義基本資料類型和引用類型來檢視一下最終生成的位元組碼有何差別。
Object[] o = new String[11];
o[0]="1aaa";
int i=o.length;
Integer[] a=new Integer[11];
a[0]=100;
int j=a.length;
int[] b=new int[11];
b[0]=100;
int k=b.length;
}
對應的位元組碼為:
2 anewarray #12 <java/lang/String> //anewarray代表對象數組
5 astore_1
6 aload_1
7 iconst_0
8 ldc #25 <1aaa>
10 aastore
11 aload_1
12 arraylength //arraylength代表長度
13 istore_2
14 bipush 11
16 anewarray #26 <java/lang/Integer> //anewarray代表包裝類數組
19 astore_3
20 aload_3
21 iconst_0
22 bipush 100
24 invokestatic #27 <java/lang/Integer.valueOf>
27 aastore
28 aload_3
29 arraylength
30 istore 4
32 bipush 11
34 newarray 10 (int) //newarray代表基本數組類型數組
36 astore 5
38 aload 5
40 iconst_0
41 bipush 100
43 iastore
44 aload 5
46 arraylength
47 istore 6
49 return
注意:定義并初始化一個數組後,在記憶體中配置設定了兩個空間,一個用于存放數組的引用變量,另一個用于存放數組本身。
進行程式開發時,要深入底層的運作機制。
看待一個數組時,一定要把數組看成兩個部分:一部分是數組引用,也就是在代碼中定義的數組引用變量;還有一部分是實際的數組對象,這部分是在對記憶體裡運作的,通常無法直接通路它,隻能通過數組引用變量來通路。
四、Array 的 length 域相關
在很多的資料中都寫了,Array中有類似public final int length的成員變量。但是在《Java Language Specifications》10.1. Array Types中明确寫了,length不是類型的一部分;
An array's length is not part of its type.
String[] s = new String[2];
System.out.println(s.length);
System.out.println(s.getClass().getDeclaredFields().length); try {
System.out.println(s.getClass().getDeclaredField("length"));
} catch (NoSuchFieldException e) {
System.out.println(e.toString());
}
}
可以看到length并不是Array的成員變量。
五、Java語言規範關于Array的定義
數組在Java裡是一種特殊類型,有别于普通的“類的執行個體”的對象。
10.1. Array Types
10.8. Class Objects for Arrays
Every array has an associated Class object, shared with all other arrays with the same component type.Although an array type is not a class, the Class object of every array acts as if:
1、The direct superclass of every array type is Object.
2、Every array type implements the interfaces Cloneable and java.io.Serializable.
數組類型是由JVM從元素類型合成出來的。
10.7. Array Members
The members of an array type are all of the following:
1、The public final field length, which contains the number of components of the array. length may be positive or zero.
從Java語言到Class檔案,Java源碼編譯器會識别出對數組類型的length字段的通路,并生成對應的位元組碼。
以OpenJDK8的javac為例:
jdk8u/jdk8u/langtools: 84eb51777733 src/share/classes/com/sun/tools/javac/jvm/Gen.java
if (sym == syms.lengthVar) {
code.emitop0(arraylength);
result = items.makeStackItem(syms.intType);
}
六、資料應用場景
這種資料結構使用一段連續的空間來存貯元素,是以可以直接通過索引來擷取到某個元素,而且可以通過對元素的内容進行排序,然後使用二分法查找,進而提供查找效率。其适合的場合主要是:
1、不會頻繁增删元素的場合,因為增删元素都牽涉到元素空間的重新配置設定,頻繁的記憶體配置設定操作會大幅降低操作效率。但添加操作時,可以通過預配置設定足夠的空間來優化添加時的效率。
2、屬于随機疊代器,可以随機通路任意元素。對于已排序的元素查找起來效率較高。
七、數組總結
在看數組的時候,因為class是動态建立的,是以看了很久,但是根據數組的特性,基本可以認為數組的域和方法,類似于:
public T[] clone() { try { return (T[]) super.clone();
} catch (CloneNotSupportedException e) { throw new InternalError(e.getMessage());
}
}
}
數組可以是一維數組、二維數組或多元數組。
數值數組元素的預設值為 0,而引用元素的預設值為 null。
交錯數組是數組的數組,是以,它的元素是引用類型,初始化為 null。交錯數組元素的次元和大小可以不同。
數組的索引從 0 開始,如果數組有 n 個元素,那麼數組的索引是從 0 到(n-1)。
數組元素可以是任何類型,包括數組類型。
數組類型是從抽象基類 Array 派生的引用類型。
課程擴充閱讀:
https://www.roncoo.com/view/1160515822987776001