Java 基礎之(十一)一維陣列
說明
陣列是程式語言中最常見的一種資料結構,可用於儲存多個數據,每個陣列元素存放一個數據,通常可通過陣列元素的索引來訪問陣列元素,包括為陣列元素賦值和取出陣列元素的值。
陣列也是一種資料型別,它本身是一種引用型別。
Java的陣列要求所有的陣列元素具有相同的資料型別,包括基本資料型別和引用資料型別。一旦陣列的初始化完成,陣列在記憶體中所佔的空間也被固定下來,因此陣列的長度將不可改變。即使把某個陣列元素的資料全部清空,但它所佔的空間依然被保留,依然屬於該陣列,該陣列的長度依然不變。
定義陣列
type[] arrayName;
注意:定義陣列時不能定義陣列長度。
陣列的初始化
Java中陣列必須先初始化,然後才可以使用。初始化就是為陣列元素分配記憶體空間,併為每個陣列元素賦初始值。
- 靜態初始化
arrayName = new Type[]{element1,element2,element3,element4...};
例如:
int[] intArray;
intArray = new int[]{5,6,8,10};
也可簡化為如下格式:
int[] intArray = {5,6,8,10};
- 動態初始化
動態初始化只指定陣列的長度,由系統為每個陣列元素指定初始值。語法格式如下:
arrayName = new type [length];
例如:
int[] intArray = new int[5];
使用陣列
陣列最常用的用法就是訪問陣列元素,包括為陣列元素賦值和取出陣列元素的值。
Java語言的陣列索引是從0開始的,最後一個數組元素的索引值陣列長度減一。
//輸出int陣列的第三個元素,將輸出整數8
System.out.println(intArray[2]);
//為int陣列的第一個陣列賦值
intArray[0] = 1;
使用length屬性可以訪問到陣列的長度,一旦獲得了陣列的長度就可以通過迴圈來遍歷該陣列的每個陣列元素。舉個簡單例子:
int[] intArray = new int[5];
for(int i = 0;i<intArray.length;i++){
System.out.println(intArray[i]);
}
執行上面程式碼將輸出5個0,因為intArray陣列執行的是預設初始化,陣列元素是int型別,系統為int型別的陣列元素賦值為0;
foreach迴圈(輸出陣列元素方法)
foreach迴圈遍歷陣列和集合。使用foreach遍歷陣列和集合時,無需獲得陣列和集合的長度,無需根據索引來訪問陣列元素和集合元素,foreach迴圈自動遍歷陣列和集合的每個元素。用法如下:
public class TestOne {
public static void main(String[] args){
int[] intArray = {2,5,8,4,6};
//使用foreach迴圈來遍歷陣列元素,其中i會自動迭代每個陣列元素
for(int i:intArray){
System.out.print(" "+i);
}
}
}
輸出結果:
2 5 8 4 6
foreach迴圈是輸出陣列元素的一種較為方便的方法。但是若想以陣列形式輸出陣列元素則可使用以下方法:
public class TestOne {
public static void main(String[] args){
int[] intArray = {1,5,3,4,8,10};
String arr = Arrays.toString(intArray);
System.out.print(arr);
}
}
輸出結果:
[1, 5, 3, 4, 8, 10]
記憶體中的陣列
因為陣列變數是引用型別變數,實際的陣列物件存放在堆記憶體中,如果引用該陣列物件的陣列引用變數是一個區域性變數,那麼它被儲存在棧記憶體中。陣列在記憶體中的儲存如圖所示:
如果需要訪問堆記憶體中的陣列元素,則程式只能通過p[index]的形式實現。也就是說,陣列引用變數是訪問堆記憶體中陣列元素的根本方式。如果堆記憶體中陣列不再有任何引用變數指向自己,則這個陣列將成為垃圾,該陣列所佔的記憶體將被系統得垃圾回收機制回收。
public class TestOne {
public static void main(String[] args){
//定義並初始化陣列,使用靜態初始化
int[] a = {2,5,8};
//定義並初始化陣列,使用動態初始化
int[] b = new int[4];
System.out.println("b陣列的長度為:"+b.length);
for(int i:a){
System.out.println(" "+i);
}
for(int j = 0;j<b.length;j++){
System.out.println(b[j]);
}
//因為a是int[]型別,b是int[]型別,所以可將a賦給b
//也就是讓b引用指向a引用指向的地址
b = a;
System.out.println("b陣列的長度為:"+b.length);
}
}
輸出結果:
b陣列的長度為:4
2
5
8
0
0
0
0
b陣列的長度為:3
從結果來看b陣列的長度似乎發生了變化,但是需要記住:
定義並初始化一個數組後,在記憶體中分配了兩個空間,一個用於存放陣列的引用變數,另一個用於存放陣列本身。
下面的示意圖說明了上面程式的執行過程:
當程式定義並初始化了a、b兩個陣列後,系統記憶體中實際產生了4塊記憶體區,其中棧記憶體中有兩個引用變數:a和b;堆記憶體中也有兩塊記憶體區,分別用於儲存a和b引用所指向的陣列本身。
從圖中可以看出a變數所引用的陣列長度是3,b變數所引用的陣列長度是4。當執行b=a時,系統將會把a的值賦給b,a和b都是引用型別變數,儲存的是地址。因此把a的值賦給b後,就是讓b指向a所指向的地址。此時計算機記憶體的儲存示意圖如下:
從圖中可以看出,當執行了b=a之後,堆記憶體中的第一個陣列具有了a、b兩個引用。此時第二個陣列失去了引用,則將被系統回收,但它的長度不會變,直到它徹底消失。
基本型別陣列的初始化
對於基本資料型別而言,陣列元素的值直接儲存在對應的陣列元素中,因此初始化陣列時,先為陣列分配記憶體空間,然後直接將陣列元素的值存入對應的陣列元素中。例如:
public class TestOne {
public static void main(String[] args){
//動態初始化陣列,陣列長度為6
int[] a = new int[6];
for(int i = 0;i<a.length;i++){
a[i] = i+10;
System.out.println(a[i]);
}
}
}
動態初始化陣列後的儲存示意圖如下所示:
顯式指定每個陣列元素值後的儲存示意圖如下所示:
引用型別陣列的初始化
引用型別陣列的陣列元素是引用。每個陣列元素裡儲存的還是引用,它指向另一塊記憶體,這塊記憶體裡儲存了有效資料。看下面的例子:
public class Person {
public int age;
public double height;
void info(){
System.out.println("My age is "+ age+",my height is "+height);
}
}
public class TestOne {
public static void main(String[] args){
//定義一個students陣列變數,其型別是Person[];並執行動態初始化
Person[] students = new Person[2];
//建立一個Person物件
Person zhang = new Person();
//為zhang所引用的Person物件的age、height賦值
zhang.age = 15;
zhang.height = 158.0;
//建立一個Person物件
Person lee = new Person();
//為lee所引用的Person物件的age、height賦值
lee.age = 16;
lee.height = 161.0;
//將zhang變數的值賦給第一個陣列元素
students[0] = p1;
//將lee變數的值賦給第二個陣列元素
students[1] = p2;
//下面兩行程式碼輸出結果一樣,因為student[0]和zhang指向的是同一個Person例項
students[0].info();
lee.info();
}
}
輸出結果:
My age is 10,my height is 172.0
My age is 20,my height is 180.0
動態初始化studentss陣列後的記憶體儲存示意圖:
建立兩個Person例項後的儲存示意圖:
此時students陣列的兩個陣列元素依然為null,直到程式依次將zhang、lee賦給students陣列的兩個陣列元素,此時這兩個陣列元素將指向有效的記憶體區。為陣列元素賦值後的記憶體儲存示意圖:
此時,從圖中可以看出,zhang和students[0]指向的是同一個記憶體區。