1. 程式人生 > >我的JAVA筆記004----JAVA陣列

我的JAVA筆記004----JAVA陣列

我的JAVA筆記

----------------------------------第一章JAVA陣列
----------------------------------2018.12.23

1.什麼是陣列?

  • 程式=演算法+資料結構
    流程控制解決的問題即為演算法問題。資料結構,就是把資料按照特定的某種結構來儲存,陣列就是一種基本的資料結構
  • 陣列:相同資料型別的元素組成的集合。元素按照線性順序排列,即除了第一個和最後一個元素外,每個元素都有前驅和後繼。
  • Java語言是典型的靜態語言,因此Java陣列是靜態的,即當陣列被初始化之後,該陣列所佔的記憶體空間和陣列長度都是不可改變
    的。

2.定義陣列(宣告陣列變數)

根據陣列元素的資料型別,可以將其分為基本型別陣列和引用型別陣列。
Java語言支援兩種語法格式來定義陣列:
type[ ] arrayName;
type arrayName[ ]; (不建議使用,為了和C語法相容的寫法)

  • 基本型別陣列
  • 引用型別陣列
public class TEST02 {

	public static void main(String[] args) {	
		Person[] students;
		System.out.println("動態初始化");
		students = new Person[2];
		Person zhang = new Person();
		zhang.age = 19;
		zhang.height = 180;
		zhang.info();
		Person lee = new Person();
		lee.age = 20;
		lee.height = 165;
		lee.info();
		System.out.println("陣列變數指向Person物件");
		students[0] = zhang;
		students[1] = lee;
		students[0].info();
		students[1].info();		
		System.out.println("陣列變數修改Person物件的屬性");
		students[0].age = 20;
		students[0].info();
	}

}
class Person{
	public int age;
	public double height;
	public void info() {
		System.out.println("我的年齡是:"+age+",我的身高是:"+height);
	}
}

此時,完成陣列定義,但暫時還沒初始化。
陣列是一種引用型別的變數,因此使用它定義一個變數時,僅僅表示定義了一個引用變數(也就是定義了一個指標),這個引用變數還未指向任何有效的記憶體,因此定義陣列時不能指定陣列的長度(初始化的工作任務)。

3.陣列初始化

包括兩種方式:靜態初始化和動態初始化。

靜態初始化

  • 完整寫法:
    宣告陣列變數與陣列初始化分開寫
    type[] arrayName;
    arrayName= new type[]{data1,data2,data3};
    宣告陣列變數時初始化
    type[] arrayName = new type[]{data1,data2,data3};

其中,arrayName為陣列變數;{data1,data2,data3}為陣列物件;data1,data2,data3為陣列元素。

  • 簡化形式:
    type[] arrayName = {data1,data2,data3};(這種寫法只能在宣告陣列變數時同時初始化)

動態初始化

type[] arrayName = new type[ length];

對於靜態初始化方式,無須指定陣列長度,指定了陣列的陣列元素,由系統來決定該陣列的長度即可。
對於動態初始化,僅需要指定陣列的長度(new關鍵字分配空間時需指定分配的間大小),即為每個陣列元素指定所需的記憶體空間,系統將為這些元素分配初始值。

基本型別:

  • 整數型別(byte、short、int、long)則陣列元素的初始值為0;
  • 浮點型別(float、double)則陣列元素的初始值為0.0;
  • 布林型別(boolean)則陣列元素的初始值為false;
  • 字元型別(char)則陣列元素的初始值為’\u0000’;

引用型別:

  • 引用型別(類、介面、陣列)則陣列元素的初始值為null。

4.使用陣列

4.1陣列元素的訪問、遍歷

  • 訪問

陣列物件的大小是固定的,長度為n,下標範圍為0~n-1。
通常使用arrayName[下標]訪問陣列元素,當下標範圍不在0~n-1時出現數組索引越界異常(執行時異常)

編譯時異常和執行時異常,簡單來說就是編譯時異常就是在寫程式碼時就會提示異常,執行時異常是在執行之後異常。

  • 遍歷,輸出陣列

(1)普通for迴圈
.length屬性,獲取陣列物件長度。

		for(int i=0;i<arrayName.length;i++) {
			System.out.println(arrayName[i]);
		}

(2)foreach迴圈(Java5)
無須獲取陣列長度,無須迴圈迭代條件,無須根據索引來訪問陣列元素,由系統完成。

		for(type variableName :arrayName) {
			System.out.println(variableName);
		}

variableName為一個臨時變數,系統依次把陣列元素賦給這個臨時變數,這個臨時變數不是陣列元素(僅儲存),所以不能對其進行賦值修改陣列。
(3).toString()

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

4.2複製

首先為什麼要複製呢?
因為單純的陣列賦值無法使陣列物件隔離,這樣子容易使陣列物件不小心被修改。

4.2.1賦值

	public static void main(String[] args) {
		
		int[] arr1 = new int[]{1,2,3,4};
		int[] arr2 = arr1;
		System.out.println("arr1:"+Arrays.toString(arr1));
		System.out.println("arr2:"+Arrays.toString(arr2));
		arr2[0]=0;
		System.out.println("arr1:"+Arrays.toString(arr1));
		System.out.println("arr2:"+Arrays.toString(arr2));
	}

執行結果為:
arr1:[1, 2, 3, 4]
arr2:[1, 2, 3, 4]
arr1:[0, 2, 3, 4]
arr2:[0, 2, 3, 4]
以上為陣列賦值,兩個引用變數指向同一個陣列物件,兩個引用變數均可以修改陣列元素的值。
修改arr2的陣列元素,會使該陣列物件的元素值被修改,所以會發現arr1的列印結果和arr2一樣。
如此,陣列物件沒有隔離性,不小心就會被修改。

4.2.2拷貝

拷貝:實現陣列物件隔離性

	public static void main(String[] args) {
		
		int[] arr1 = new int[]{1,2,3,4};
		int[] arr3 = new int[arr1.length];
		System.out.println("初始化::");
		System.out.println("arr1:"+Arrays.toString(arr1));
		System.out.println("arr3:"+Arrays.toString(arr3));
		for(int i=0;i<arr1.length;i++) {
			arr3[i] = arr1[i];
		}
		System.out.println("拷貝後:");
		System.out.println("arr1:"+Arrays.toString(arr1));
		System.out.println("arr3:"+Arrays.toString(arr3));
		arr3[0]=0;
		System.out.println("修改arr3陣列元素值:");
		System.out.println("arr1:"+Arrays.toString(arr1));
		System.out.println("arr3:"+Arrays.toString(arr3));

	}

結果為:
初始化::
arr1:[1, 2, 3, 4]
arr3:[0, 0, 0, 0]
拷貝後:
arr1:[1, 2, 3, 4]
arr3:[1, 2, 3, 4]
修改arr3陣列元素值:
arr1:[1, 2, 3, 4]
arr3:[0, 2, 3, 4]
可以看到arr3的修改並不會影響到arr1,這就是實現了陣列物件的隔離。
兩個陣列變數實際指向不同的陣列物件。

4.2.3陣列拷貝的其他方法

	public static void main(String[] args) {
		
		int[] src = new int[]{1,2,3,4};
		int[] dest1 = new int[src.length];
		System.arraycopy(src,0,dest1,0,src.length);
		System.out.println("src:"+Arrays.toString(src));
		System.out.println("dest1:"+Arrays.toString(dest1));
	
		int[] dest2 = Arrays.copyOf(src, src.length);
		System.out.println("dest2:"+Arrays.toString(dest2));
	}

結果為:
src:[1, 2, 3, 4]
dest1:[1, 2, 3, 4]
dest2:[1, 2, 3, 4]
需要import java.util.Arrays;
System.arraycopy(); Java API提供,底層使用C++寫的,速度快,比for迴圈實現陣列拷貝效率更高,推薦使用。
Arrays.copyOf(); JDK1.6版本提供,底層是arraycopy()方法,但需要注意JDK版本。

4.2.4利用拷貝進行陣列擴容

陣列的長度在初始化後是不可改變的,可以建立一個更大的新陣列並將小陣列複製到其中,實現“陣列擴容”。

	public static void main(String[] args) {
		int[] a = new int[] {1,2,3,4,5,6};
		System.out.println("原始a:"+Arrays.toString(a));
		System.out.println(a);
		a = Arrays.copyOf(a, a.length+5);
		System.out.println("擴容後a:"+Arrays.toString(a));
		System.out.println(a);

	}
	

結果為:
原始a:[1, 2, 3, 4, 5, 6]
[[email protected]
擴容後a:[1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0]
[[email protected]
實際上,只是將陣列變數指向另一個新的陣列物件,原始的陣列長度並沒有發生變化。

4.3陣列的記憶體管理

4.3.1基本型別陣列的初始化

程式先為陣列分配記憶體空間,再將陣列元素的值存入對應記憶體中。
int[] a;
在main方法棧區定義了一個數組變數,還未指向陣列物件。
a= new int[] {1,2,3,4,5,6};
靜態初始化之後,確定了陣列長度,將陣列元素填入堆區陣列物件的記憶體中(若是動態初始化,預設值)。
注意:
指定型別的變數只能儲存指定型別的值。
所有區域性變數都存放在棧記憶體中,不管是基本型別的變數還是引用型別的變數,都是儲存在各自的方法棧記憶體中的;但引用型別的變數所引用的物件(包括陣列、普通的Java物件)則是儲存在堆記憶體中。

4.3.2引用型別陣列的初始化

引用型別陣列的陣列元素依然是引用型別的,因此陣列元素裡儲存的還是引用,它指向另一塊記憶體,這塊記憶體裡儲存了該引用變數所引用的物件。

4.4易混淆的點

4.4.1陣列物件和陣列變數

陣列變數只是一個引用變數,陣列物件就是儲存在堆記憶體中的連續記憶體空間。所以陣列初始化並不是對陣列變數執行初始化,而是在堆記憶體中建立陣列物件(為該陣列物件分配一塊連續的記憶體空間)。
就像是一個數組物件可以看做一間房間,我們可以給它取不同的名字,但是我要是把這個名字掛到另一個房間去,那這個名字對應的就是另一間房間了,也就是指向了另一個數組物件(陣列賦值)。當然,我一個房間可以取很多名字,但是它們中哪怕一個一旦修改了這個房間的屬性,那麼別的名字看到的房間屬性也就變了,這就是陣列物件的不隔離性。
從記憶體來看,陣列變數儲存在棧記憶體區,陣列物件儲存在堆記憶體區,兩者是不一樣的。

4.4.2陣列長度

只要型別相互相容,可以讓一個數組變數指向另一個實際的陣列,這種操作會產生陣列的長度可變的錯覺(陣列物件改變了)。
由於陣列變數整體賦值(上面講的陣列擴容)導致的陣列的長度可以改變,只是一個假相(因為陣列物件不是同一個物件)。

4.4.3多維陣列

Java實際上沒有多維陣列,所謂多維陣列其實就是陣列元素依然是陣列的一維陣列,N維陣列是陣列元素為(N-1)維陣列的陣列。