1. 程式人生 > >Java程式設計思想 第十六章:陣列

Java程式設計思想 第十六章:陣列

對陣列的看法,你可以建立並組裝他們,通過使用整形索引值可以訪問他們中的元素,並且他們的尺寸不能發生改變。

1. 陣列為什麼特殊

陣列與其他種類的容器之間的區別有三方面:

  1. 效率,陣列是效率最高的儲存和隨機訪問物件引用序列的方式。陣列就是一個簡單線性序列,使得元素訪問非常快速。代價就是陣列物件的大小被固定,並且在生命週期內不可改變。
  2. 型別,泛型之前,其他容器類在處理物件時,都將視作沒有任何型別,也就是說,這些物件都將當作Object處理。陣列之所以優於泛型之前的容器,就是可以建立一個持有具體型別的陣列。
  3. 型別,泛型之前,其他容器類在處理物件時,都將視作沒有任何型別,也就是說,這些物件都將當作Object處理。陣列之所以優於泛型之前的容器,就是可以建立一個持有具體型別的陣列。

2.陣列是第一級物件

陣列識別符號其實只是一個引用指向在堆中建立的一個真實物件,這個陣列物件用以儲存指向其他物件的引用。物件陣列和基本型別陣列的區別主要是:物件陣列儲存的是引用,基本型別陣列儲存的是基本型別的值。

下面我們來講解陣列的幾種建立方式:

import java.util.*;
import static net.mindview.util.Print.*;

public class ArrayOptions {
  public static void main(String[] args) {
    // Arrays of objects:
    BerylliumSphere[
] a; // Local uninitialized variable BerylliumSphere[] b = new BerylliumSphere[5]; // The references inside the array are // automatically initialized to null: print("b: " + Arrays.toString(b)); BerylliumSphere[] c = new BerylliumSphere[4]; for(int i = 0; i < c.length; i++) if
(c[i] == null) // Can test for null reference c[i] = new BerylliumSphere(); // Aggregate initialization: BerylliumSphere[] d = { new BerylliumSphere(), new BerylliumSphere(), new BerylliumSphere() }; // Dynamic aggregate initialization: a = new BerylliumSphere[]{ new BerylliumSphere(), new BerylliumSphere(), }; // (Trailing comma is optional in both cases) print("a.length = " + a.length); print("b.length = " + b.length); print("c.length = " + c.length); print("d.length = " + d.length); a = d; print("a.length = " + a.length); // Arrays of primitives: int[] e; // Null reference int[] f = new int[5]; // The primitives inside the array are // automatically initialized to zero: print("f: " + Arrays.toString(f)); int[] g = new int[4]; for(int i = 0; i < g.length; i++) g[i] = i*i; int[] h = { 11, 47, 93 }; // Compile error: variable e not initialized: //!print("e.length = " + e.length); print("f.length = " + f.length); print("g.length = " + g.length); print("h.length = " + h.length); e = h; print("e.length = " + e.length); e = new int[]{ 1, 2 }; print("e.length = " + e.length); } } 執行結果: b: [null, null, null, null, null] a.length = 2 b.length = 5 c.length = 4 d.length = 3 a.length = 3 f: [0, 0, 0, 0, 0] f.length = 5 g.length = 4 h.length = 3 e.length = 3 e.length = 2

陣列建立的方式:

  1. 定義區域性變數BerylliumSphere[] a;
  2. 定於固定大小的陣列物件 BerylliumSphere[] b = new BerylliumSphere[5];
  3. 聚集初始化語法
 BerylliumSphere[] d = { new BerylliumSphere(),
      new BerylliumSphere(), new BerylliumSphere()
    };
  1. 動態聚集初始化語法
 a = new BerylliumSphere[]{
      new BerylliumSphere(), new BerylliumSphere(),
    };

3. 返回一個數組

在Java中,直接返回一個數組,無須擔心為陣列負責——只要需要它,就會一直粗壯乃,當用完了垃圾回收器會清理掉它。

Arrays.toString(a);

4.多維陣列

使用花括號將每個向量分隔開:

public class MultidimensionalPrimitiveArray {
  public static void main(String[] args) {
    int[][] a = {
      { 1, 2, 3, },
      { 4, 5, 6, },
    };
    System.out.println(Arrays.deepToString(a));
  }
} /*
[[1, 2, 3], [4, 5, 6]]
*/

JavaSE5的Array.deepToString方法,可以將多維陣列轉換為多個String:

public class ThreeDWithNew {
  public static void main(String[] args) {
    // 3-D array with fixed length:
    int[][][] a = new int[2][2][4];
    System.out.println(Arrays.deepToString(a));
  }
} /*
[[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0]]]
*/

5. 陣列與泛型

通常陣列與泛型不能很好地結合。可以引數化陣列本身的型別:

class ClassParameter<T> {
  public T[] f(T[] arg) { return arg; }
}

class MethodParameter {
  public static <T> T[] f(T[] arg) { return arg; }
}

public class ParameterizedArrayType {
  public static void main(String[] args) {
    Integer[] ints = { 1, 2, 3, 4, 5 };
    Double[] doubles = { 1.1, 2.2, 3.3, 4.4, 5.5 };
    Integer[] ints2 = new ClassParameter<Integer>().f(ints);
    Double[] doubles2 = new ClassParameter<Double>().f(doubles);
    ints2 = MethodParameter.f(ints);
    doubles2 = MethodParameter.f(doubles);
  }
}

引數化方法不必為需要應用的每種不同的型別都是用一個引數去例項化,並且可以將其定義為靜態的。當然,不能總選擇使用引數化方法而不是引數化類,但是他應該是首選。

不能建立泛型陣列這一說法並不準確,編譯器確實不讓例項化泛型陣列,但允許建立對這種陣列的引用:List[] ls(可通過編譯器而不報任何錯誤)。
儘管不能建立實際的持有泛型的陣列物件,但可以建立非泛型陣列然後轉型:

一旦擁有了對List[]的引用,你就會看到某些編譯器檢查。問題是陣列是協變型別的,因此List[]也是一個Object[],並且可以利用這一點,將一個ArrayList賦值到陣列中,而不會有任何編譯器或執行時錯誤。
在類或方法的內部,擦除通常會使泛型變得不適用。例如,你不能建立泛型陣列:

public class ArrayOfGenericType<T> {
  T[] array; // OK
  @SuppressWarnings("unchecked")
  public ArrayOfGenericType(int size) {
    //! array = new T[size]; // 不合法的
    array = (T[])new Object[size]; // "unchecked" Warning
  }
  // Illegal:
  //! public <U> U[] makeArray() { return new U[10]; }
}

擦除再次成為了障礙——本例試圖建立的類已被擦除,因而是型別未知的陣列。注意,可以建立Object陣列,然後將其轉型,但是如果沒有@SuppressWarnings註解,將在編譯期得到一個不受檢查的警告資訊,因為這個陣列沒有真正持有或動態檢查型別T。也就是說,如果建立一個String[],Java在編譯期和執行時都會強制要求將String物件置於該陣列中。但是如果建立的是Object[],那就可以將除基本型別之外的任何物件置於該陣列中。

6. 建立測試資料

Arrays.fill()

fill()方法:只能用同一個值填充各個位置,而針對物件而言,就是複製同一個引用來進行填充:

7. Arrays實用功能

java.util.Arrays類:

  1. equals比較兩個陣列是否相等(deepEquals用於多維陣列)
  2. fill 填充陣列中的每一個元素
  3. sort用於陣列排序
  4. binarySearch用於已經排序的陣列中查詢元素
  5. toString
  6. hashCode產生陣列的雜湊碼

Arrays.asList接受任意的序列或陣列做為引數,應將其轉化為List容器

7.1 複製陣列

Java標準類庫提供static方法System.arraycopy,用它複製陣列比用for迴圈複製要快很多,並針對所有型別做了過載。

 System.arraycopy(k, 0, i, 0, k.length);

arraycopy需要引數有:源陣列,表示從源陣列中的什麼位置開始複製的偏移量,表示從目標陣列的什麼位置開始複製的偏移量,以及需要複製的元素個數。
然而如果複製物件陣列,那麼只是複製了物件的引用——而不是物件本身的拷貝。這被稱作淺複製。System.arraycopy不會執行自動包裝和自動拆包,兩個陣列必須具有相同的確切型別。

7.2 陣列的比較

過載後的equals,陣列相等的條件是元素個數必須相等,並且對應位置的元素也相等,這可以通過對每一個元素使用equals作比較來判斷,對於基本型別需要使用基本型別的包裝器類的equals方法。

Arrays.equals(s1, s2)

7.3 陣列元素的比較

程式設計的基本目的是將保持不變的事物與會發生改變的事物相分離,而這裡,不變的是通用的排序演算法,改變的是各種物件相互比較的方式。因此,不是將進行比較的程式碼編寫成不同的子程式,而是使用策略設計模式。

Java有兩種比較功能:

  1. java.lang.Comparable介面的comparaTo方法,此方法接受另一個Object為引數,如果當前物件小於引數則返回負值,相等返回0,大於返回正值。
  2. Arrays.sort(a, Collections.reverseOrder());

7.4 陣列排序

使用內建的排序方法就可以對任意基本型別陣列排序;也可以對任意物件陣列排序,只要物件實現了Comparable或具有相關聯的Comparator:

Arrays.sort(sa);

String排序演算法依據字典編排數序排序,所以大寫字母開頭的詞都放在前面輸出,然後才是小寫。如果忽略大小寫,使用CASE_INSENSITIVE_ORDER。

7.5 在已排序的陣列中查詢

陣列已經排好序,可以使用Arrays.binarySearch執行快速查詢。如果對未排序的陣列使用,那麼將發生不可預料的結果。

Arrays.binarySearch(a, r);

如果找到目標,Arrays.binarySeach產生的返回值等於或大於0。否則產生負值,表示如要保持陣列的排序狀態此目標元素所應該插入的位置。這個負值的計算方式是: - (插入點) - 1
插入點是指,第一個大於查詢物件的呀un蘇在陣列中的位置,如果陣列中所有元素都小於要查詢的物件,插入點就等於a.size()。

如果陣列包含重複元素,則無法保證找到的是副本中的哪一個。

如果使用Comparator排序了物件陣列(基本型別陣列無法使用),在使用binarySearch時必須提供同樣的Comparator: