數組是相同類型的、用一個標識符名稱封裝到一起的一個對象序列或基本類型數據序列。可以使用整型索引值訪問它們的元素(“[]”語法是訪問數組唯一的方式),並且它們的尺寸不能改變。
對象數組和基本類型數組在使用上幾乎是相同的,唯一的區別就是對象數組保存的是引用,基本類型數組直接保存基本類型的值。
數組標識符其實只是個引用,指向在堆中創建的一個真實對象,這個(數組)對象用以保存指向其他對象的引用。
數組定義與初始化
數組定義
定義一個數組,只需在類型名後加上一對空方括號。
int[] a; // 更合理,表明類型是 “一個int型數組”
或者,方括號也可以放在標識符後面。
int a[];
現在擁有的只是對數組的一個引用(已經爲該引用分配了足夠的存儲空間),並沒有給數組對象本身分配任何空間。
爲了給數組創建相應的存儲空間,必須寫初始化表達式。
數組初始化
1)花括號初始化
有一種特殊的初始化表達式,由一對花括號括起來的值組成,它必須在創建數組的地方出現(定義的同時進行初始化)
int[] a = {1,2,3,4};
2)new初始化
如果創建的是基本類型的數組,數組元素中的基本數據類型值會自動初始化成空值(對於數字/字符就是0,對於布爾型就是false)
Random rand = new Random(47); //Random.nextInt()方法會隨機返回0到輸入參數之間的一個值
int[] a = new int[rand.nextInt(20)]; //定義的同時進行初始化
如果創建的是非基本類型的數組,那就創建了一個引用數組。默認情況下其中所有的引用被自動初始化爲null。之後可以通過創建對象,並把對象賦值給引用來完善初始化過程。
Random rand = new Random(47); //Random.nextInt()方法會隨機返回0到輸入參數之間的一個值
Integer[] a = new Integer[rand.nextInt(20)]; //整型的包裝器類 Integer,它是一個類而不是基本類型
for (int i = 0; i < a.length; i++)
a[i] = rand.nextInt(500); //創建新的Integer對象,本例是通過自動包裝機制創建的
也可以使用花括號括起來的列表來初始化對象數組。有兩種形式:
Integer[] a = {new Integer(1), new Integer(2), 3,}; //初始化列表的最後一個逗號可選
Integer[] a = new Integer[] {new Integer(1), new Integer(2), 3,};
//Other.main(new String[]{"abc", "de"}); //String對象數組
可變參數列表
可變參數列表可以應用於參數個數或類型未知的場合。
由於所有的類都直接或間接繼承於Object類,所以可以創建以Object數組爲參數的方法。
這裏,輸出建立的類的對象,默認行爲(如果沒有定義toString()方法的話)打印出的內容是類的名稱和對象的地址。
在Java SE5中,加入可變參數列表的特性,如下示:
有了可變參數,就不用再顯式地編寫數組語法了。當你指定參數時,編譯器實際上會爲你去填充數組,所以你獲取的仍舊是一個數組。也就是從元素列表到數組的自動轉換。如果本身就是個數組,編譯器就不會在其上執行任何轉換。
在可變參數列表中也可以使用Object之外類型的參數(也包括int等基本類型),如下面所有的可變參數都必須是String對象。
數組賦值
謹記:數組標識符其實只是個引用,指向在堆中創建的一個真實對象
數組賦值就是對指向數組的引用賦值,使之指向另一個數組對象。
多維數組
創建多維數組
可以使用花括號將每個向量分隔開:
int[][] a = {
{1, 2, 3, },
{4, 5, 6, },
};
也可以使用 new 來分配數組。在不進行顯式初始化的情況下,數組的值會被自動初始化
int [][][] a = new int[2][2][4];
粗糙數組
數組中構成矩陣的每個向量都可以具有任意的長度
二維數組獲取行數: a.length
二維數組獲取列數: a[i].length
Arrays類實用功能
Arrays類是數組的操作類,定義在 java.util 包中,主要功能是實現數組元素的查找、數組內容填充、排序、比較等
填充數組
Java類庫Arrays類提供 Arrays.fill(),用來填充數組。
Arrays.fill()方法作用十分有限,只能用同一個值填充各個位置,而針對對象而言,就是複製同一個引用進行填充。
使用Arrays.fill()可以填充整個數組,或者像示例最後一條語句所示,只填充數組的某個區域。
複製數組
Java類庫System類提供 System.arraycopy(),用它複製數組比用for循環複製要快很多。
arraycopy() 需要的參數:源數組、從源數組什麼位置開始複製的偏移量、目標數組、從目標數組什麼位置開始複製的偏移量、需要複製的元素個數
對數組的任何越界操作都會導致異常
如果是複製對象數組,那麼只是複製了對象的引用——而不是對象本身的拷貝
注:System.arraycopy() 不會執行自動包裝和自動拆包,兩個數組必須具有相同的確切類型。
數組的比較
Java類庫Arrays類提供 Arrays.equals(),用來比較整個數組。
數組相等的條件是元素個數必須相等,並且對應位置的元素也相等。這相當於對每個元素使用equals()作比較來判斷(對於基本類型,需要使用基本類型的包裝器類的equals()方法。如,對於int類型使用 Integer.equals() 作比較)。
數組元素的比較
Java有兩種方式來提供比較功能。
第一種是實現 java.lang.Comparable 接口,使類具有天生的比較能力。此接口很簡單,只有 compareTo() 一個方法。此方法接收另一個Object爲參數,如果當前對象小於參數則返回負值,如果相等則返回零,如果當前對象大於參數則返回正值。
第二種是創建一個實現了 Comparator 接口的單獨的類。這個類有 compare() 和 equals() 兩個方法。然而不一定要實現equals()方法,除非有特殊的性能需要,因爲無論何時創建一個類,都是間接繼承自Object,而Object帶有equals()方法。所以只需用默認的Object的equals()方法就可以滿足接口的要求了。
數組排序
使用 Arrays.sort() 方法,就可以對任意的基本類型數組排序;
也可以對任意的對象數組進行排序,只要 該對象實現了Comparable接口 或 具有相關聯的Comparator。
注意,String排序算法依據詞典編排順序排序,所以大寫字母開頭的詞都放在前面輸出,然後纔是小寫字母開頭的詞。
數組與容器
總結
本章看到了Java對尺寸固定的低級數組提供了適度的支持。這種數組強調的是性能而不是靈活性
在Java的初始版本中,尺寸固定的低級數組絕對是必需的。因爲Java早期版本中對容器的支持非常少。因此,選擇包含數組總是合理的。
其後的Java版本對容器的支持得到了明顯的改進,並且現在的容器在除了性能之外的各個方面都使得數組相形見絀。有了額外的自動包裝機制和泛型,在容器中持有基本類型就變得易如反掌了,而這也進一步促使用容器來替換數組。因爲泛型可以產生類型安全的容器,因此數組面對這一變化,已經變得毫無優勢了。
最初有關效率的論點總是很吸引人,但隨着時間的推移,我們看到了與這種思想背道而馳,向着使用像容器這類高級構件的方向的演化。
因此,容器幾乎總是更好的選擇。
當你使用最近的Java版本編程時,應該 “優先選擇容器而不是數組”。只有在已證明性能成爲問題,並且切換到數組對性能提高有所幫助時,你才應該將程序重構爲使用數組。