Java 基礎 - 數組


數組是編程語言中最多見的一種數據結構,能夠用於儲存多個數據,一般可經過數組元素的索引來訪問數組元素,包括數組元素賦值和取出數組元素的值.

初識數組

  • 數組也是一種類型,屬於引用數據類型.
  • 數組元素的類型是惟一的,一個數組裏只能存儲一種類型的數據.
  • 數組的長度是固定的,即一個數組一單初始化完成,數組在內存中所佔的空間將被固定下來,長度不在發生改變.即便把某個數組的元素清空,其所佔的空間依然被保留.

數組的初始化

定義數組變量

Java支持兩種語法格式定義數組:java

type[] arr;
type arr[];

對於這兩種定義而言,一般使用第一種格式來定義數組,由於第一種有更好的語義.第二種容易和變量名混淆程序員

初始化

Java 數組只有初始化以後才能使用,所謂的初始化,就是爲數組的元素分配內存空間.併爲每一個數組元素賦初始值.編程

靜態初始化

由程序員顯示的指定每一個數組原始的初始值.由系統決定數組的長度.
靜態初始化的語法格式爲:數組

type[] arr = new type[]{item1, item2, item3,...};

type 爲數組元素的數據類型, 數組元素類型必須爲 type 類型,或者其子類的實例.
除此以外,靜態初始化還有以下簡化的語法格式:數據結構

type[] arr = {item1, item2, item3 ...};

動態初始化

動態初始化只指定數組的長度,由系統爲每一個元素指定初始值,動態初始化的語法格式以下:編程語言

type[] arr = new type[length];

上面的語法中,須要指定一個 int 類型的 length 參數,這個參數指定了數組的長度.
執行動態初始化時,程序員只指定數組的長度,數組元素的初始值由系統按照以下自動分配函數

  • 數組元素類型是基本類型中的整數類型(byte, short, int, long),則數組元素的值是 0.
  • 數組元素類型是基本類型中的浮點類型(float, double),則數組元素的值是 0.0.
  • 數組元素類型是基本類型中的字符類型(char),則數組元素的值是'\u0000'.
  • 數組元素的類型是基本類型中的布爾類型(boolean),則數組元素的值是 false.
  • 數組元素的類型是引用類型(類,接口,數組),則數組元素的值是 null;

數組的訪問

數組最經常使用的方法就是訪問數組元素,包括對數組元素進行賦值和取出數組元素.3d

數組元素讀取、賦值

int[] arr = {1,2,3};
// 數組取值 經過 arr[index] 訪問 
int a = arr[0];
// arr 爲{1,3,3}
arr[1] = 3

若是訪問數組元素時指定的索引值小於0,或者大於等於數組的長度,編譯程序時不會出現任何錯誤,但運行時出現異常java.lang.ArrayIndexOutOfBoundsException:N(數組越界異常), N 就是試圖訪問的數組索引.code

數組的遍歷

for 循環

int[] arr = new int[5];
// 輸出 5 個 0
for(int i = 0; i < arr.length; i++){
    System.out.println(arr[i])
}
arr[1] = 1;
arr[2 = 2;
// 輸出 0 1 2 0 0
for(int i = 0; i < arr.length; i++){
    System.out.println(arr[i])
}

上面的代碼第一次循環輸出 5 個 0,由於 arr 數組執行的是默認初始化,數組元素是 int 類型,系統爲 int 類型的數組元素初始化賦值爲 0.對象

foreach循環

Java5 以後,Java 提供了一種更簡單的循環:foreach循環,這種循環遍歷數組和集合更加方便.

for (type item : array|collection){
    // 
}

使用foreach循環須要注意:

int[] arr = {1, 2, 3, 4, 5};
for (int item: arr){
    System.out.println(item);
    item = 0;
    System.out.println(item);
}
System.out.println(arr[0]);

上例程序將輸出

1
0
2
0
3
0
4
0
5
0
1

由輸出結果能夠看出來,在 foreach循環中對數組元素進行賦值,結果致使不能正確的遍歷數組元素.同時在循環中爲改變的數組元素的值並無真正改變數組元素,由於在 foreach中循環變量至關於一個臨時變量,系統會把數組元素一次賦值給這個臨時變量,而這個臨時變量並非數組元素,它只是保存了數組元素的值.所以要注意:若是但願改變數組元素的值,則不能使用這種 foreach 循環.

深刻了解數組

JDK 中的 Array

查看 Java源碼中的Array類能夠發現它是個 final class, 其中方法以下:
Arrays

Array類中基本都是 getXXsetXX 方法,
而且所有都爲 native 方法.使用 native關鍵字說明這個方法是原生函數,也就是這個方法是用C/C++語言實現的,而且被編譯成了DLL,由java去調用,所以咱們能夠將數組理解爲是由計算機本地方法去實現的類,並不屬於 Java.

數組的內存分佈

數組是一種引用數據類型,數組的引用變量時存儲在棧內存中的,而數組元素是在堆內存中,而且是連續存放的.這是爲了能快速存取數組元素,由於只須要移動index(內部計算物理地址:數組起始地址+index * 元素size大小)就能夠訪問,而這是很快的 O(1)。

在Java 內存模型中,數組對象被存儲在堆(heap)內存中;若是引用該數組對象的變量是一個局部變量,那麼它被存儲在棧(stack)內存中.以下圖所示:
數組的內存分佈

若是須要訪問上圖堆內存中的數組元素,在程序中只能經過 p[index]的形式實現.也就是說,數組引用變量時訪問堆內存中數組元素的根本方式.
現有以下代碼:

// 定義並靜態初始化數組
int[] a = {5, 7, 20};
// 定義數組,使用動態初始化
int[] b = new int[4];
System.out.println("b 數組的長度爲: " + b.length);
// 循環輸出 a 數組的元素
for (int i = 0, len = a.length; i < len; i++ ){
    System.out.println(a[i]);
}
// 循環輸出 b 數組的元素
for (int i = 0, len = b.length; i < len; i++ ){
    System.out.println(b[i]);
}
// 將 a 的值賦給 b,即將 b 的引用指向 a 引用指向的數組
b = a;
// 再次輸出 b 數組的長度
System.out.println("b 數組的長度爲: " + b.length);

運行上例代碼,首先會輸出 b 的長度爲 4,而後輸出 a,b 的各項元素,接着輸出 b 的長度爲 3.看起來數組的長度是可變的,其實這是一個假象.
上例代碼內存分析:

  1. 初始化 a,b 數組,在內存中產生了 4 塊區域,棧中的引用變量 a,b 以及堆中的實際數組對象. 其中 a 引用的數組對象長度爲 3, b 引用的數組長度爲 4.
    數組的內存分佈

  2. 程序執行b = a 操做.系統會將 a 的值賦給 b,即將 a 引用的數組對象的內存地址賦給 b,此時 b 的值爲 a 引用的數組對象的內存地址.
    數組的內存分佈

從上能夠看出,程序執行 b = a 以後,b 以前引用的數組對象長度並無發生任何改變,而 b 的值變成了 a 引用的數組對象的地址,此時 b 數組的長度即爲 a 數組的長度 3.

須要注意的是數組元素的內存空間是連續的,是指

  1. 若是數組元素是原始類型,那麼數組元素存放的就是原始類型的值,他們是連續存放的
  2. 若是數組元素是對象,那麼數組元素就是存放引用了,數組元素是連續存放的,而引用的對象可能在另外的地方,與數組元素可能相隔很遠,即不連續。

多維數組

Java 提供了支持多維數組的語法,可是從數組底層的運行機制上來看,並不存在多維數組.
多維數組的定語語法爲

type[][] arr = new type[length1][length2]

length2可動態建立.
二維數組本質就是一位數組中的每一個元素都是一個一維數組. 如上length2給出了值,則初始化了一維數組中的每一個元素都是一個長度爲length2的一維數組.其內存模型爲:
二維數組內存分佈

相關文章
相關標籤/搜索