深入理解 Java 陣列

靜默虛空發表於2019-03-13

:notebook: 本文已歸檔到:「blog

:keyboard: 本文中的示例程式碼已歸檔到:「javacore

簡介

陣列的特性

陣列對於每一門程式語言來說都是重要的資料結構之一,當然不同語言對陣列的實現及處理也不盡相同。幾乎所有程式設計語言都支援陣列。

陣列代表一系列物件或者基本資料型別,所有相同的型別都封裝到一起,採用一個統一的識別符號名稱。

陣列的定義和使用需要通過方括號 []

Java 中,陣列是一種引用型別。

Java 中,陣列是用來儲存固定大小的同型別元素。

陣列和容器

Java 中,既然有了強大的容器,是不是就不需要陣列了?

答案是不。

誠然,大多數情況下,應該選擇容器儲存資料。

但是,陣列也不是毫無是處:

  • Java 中,陣列是一種效率最高的儲存和隨機訪問物件引用序列的方式。陣列的效率要高於容器(如 ArrayList)。
  • 陣列可以持有值型別,而容器則不能(這時,就必須用到包裝類)。

Java 陣列的本質是物件

Java 陣列的本質是物件。它具有 Java 中其他物件的一些基本特點:封裝了一些資料,可以訪問屬性,也可以呼叫方法。所以,陣列是物件。

如果有兩個類 A 和 B,如果 B 繼承(extends)了 A,那麼 A[] 型別的引用就可以指向 B[] 型別的物件。

擴充套件閱讀:Java 中陣列的特性

如果想要論證 Java 陣列本質是物件,不妨一讀這篇文章。

Java 陣列和記憶體

Java 陣列在記憶體中的儲存是這樣的:

陣列物件(這裡可以看成一個指標)儲存在棧中。

陣列元素儲存在堆中。

如下圖所示:只有當 JVM 執行 new String[] 時,才會在堆中開闢相應的記憶體區域。陣列物件 array 可以視為一個指標,指向這塊記憶體的儲存地址。

深入理解 Java 陣列

宣告陣列

宣告陣列變數的語法如下:

int[] arr1; // 推薦風格
int arr2[]; // 效果相同
複製程式碼

建立陣列

Java 語言使用 new 操作符來建立陣列。有兩種建立陣列方式:

  • 指定陣列維度
    • 為陣列開闢指定大小的陣列維度。
    • 如果陣列元素是基礎資料型別,會將每個元素設為預設值;如果是引用型別,元素值為 null
  • 不指定陣列維度
    • 用花括號中的實際元素初始化陣列,陣列大小與元素數相同。

示例 1:

public class ArrayDemo {
    public static void main(String[] args) {
        int[] array1 = new int[2]; // 指定陣列維度
        int[] array2 = new int[] { 1, 2 }; // 不指定陣列維度

        System.out.println("array1 size is " + array1.length);
        for (int item : array1) {
            System.out.println(item);
        }

        System.out.println("array2 size is " + array1.length);
        for (int item : array2) {
            System.out.println(item);
        }
    }
}
// Output:
// array1 size is 2
// 0
// 0
// array2 size is 2
// 1
// 2
複製程式碼

:bulb: 說明 請注意陣列 array1 中的元素雖然沒有初始化,但是 length 和指定的陣列維度是一樣的。這表明指定陣列維度後,無論後面是否初始化陣列中的元素,陣列都已經開闢了相應的記憶體

陣列 array1 中的元素都被設為預設值。

示例 2:

public class ArrayDemo2 {
    static class User {}

    public static void main(String[] args) {
        User[] array1 = new User[2]; // 指定陣列維度
        User[] array2 = new User[] {new User(), new User()}; // 不指定陣列維度

        System.out.println("array1: ");
        for (User item : array1) {
            System.out.println(item);
        }

        System.out.println("array2: ");
        for (User item : array2) {
            System.out.println(item);
        }
    }
}
// Output:
// array1:
// null
// null
// array2:
// io.github.dunwu.javacore.array.ArrayDemo2$User@4141d797
// io.github.dunwu.javacore.array.ArrayDemo2$User@68f7aae2
複製程式碼

:bulb: 說明

請將本例與示例 1 比較,可以發現:如果使用指定陣列維度方式建立陣列,且陣列元素為引用型別,則陣列中的元素元素值為 null

陣列維度的形式

建立陣列時,指定的陣列維度可以有多種形式:

  • 陣列維度可以是整數、字元。
  • 陣列維度可以是整數型、字元型變數。
  • 陣列維度可以是計算結果為整數或字元的表示式。

示例:

public class ArrayDemo3 {
    public static void main(String[] args) {
        int length = 3;
        // 放開被注掉的程式碼,編譯器會報錯
        // int[] array = new int[4.0];
        // int[] array2 = new int["test"];
        int[] array3 = new int['a'];
        int[] array4 = new int[length];
        int[] array5 = new int[length + 2];
        int[] array6 = new int['a' + 2];
        // int[] array7 = new int[length + 2.1];
        System.out.println("array3.length = [" + array3.length + "]");
        System.out.println("array4.length = [" + array4.length + "]");
        System.out.println("array5.length = [" + array5.length + "]");
        System.out.println("array6.length = [" + array6.length + "]");
    }
}
// Output:
// array3.length = [97]
// array4.length = [3]
// array5.length = [5]
// array6.length = [99]
複製程式碼

? 說明

當指定的陣列維度是字元時,Java 會將其轉為整數。如字元 a 的 ASCII 碼是 97。

綜上,Java 陣列的陣列維度可以是常量、變數、表示式,只要轉換為整數即可

請留意,有些程式語言則不支援這點,如 C/C++ 語言,只允許陣列維度是常量。

陣列維度的大小

陣列維度並非沒有上限的,如果數值過大,編譯時會報錯。

int[] array = new int[6553612431]; // 陣列維度過大,編譯報錯
複製程式碼

此外,陣列過大,可能會導致棧溢位

訪問陣列

Java 中,可以通過在 [] 中指定下標,訪問陣列元素,下標位置從 0 開始。

public class ArrayDemo4 {
    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        for (int i = 0; i < array.length; i++) {
            array[i]++;
            System.out.println(String.format("array[%d] = %d", i, array[i]));
        }
    }
}
// Output:
// array[0] = 2
// array[1] = 3
// array[2] = 4
複製程式碼

? 說明

上面的示例中,從 0 開始,使用下標遍歷陣列 array 的所有元素,為每個元素值加 1 。

陣列的引用

Java 中,陣列型別是一種引用型別

因此,它可以作為引用,被 Java 函式作為函式入參或返回值

陣列作為函式入參的示例:

public class ArrayRefDemo {
    private static void fun(int[] array) {
        for (int i : array) {
            System.out.print(i + "\t");
        }
    }

    public static void main(String[] args) {
        int[] array = new int[] {1, 3, 5};
        fun(array);
    }
}
// Output:
// 1	3	5
複製程式碼

陣列作為函式返回值的示例:

public class ArrayRefDemo2 {
    /**
     * 返回一個陣列
     */
    private static int[] fun() {
        return new int[] {1, 3, 5};
    }

    public static void main(String[] args) {
        int[] array = fun();
        System.out.println(Arrays.toString(array));
    }
}
// Output:
// [1, 3, 5]
複製程式碼

泛型和陣列

通常,陣列和泛型不能很好地結合。你不能例項化具有引數化型別的陣列。

Peel<Banana>[] peels = new Pell<Banana>[10]; // 這行程式碼非法
複製程式碼

Java 中不允許直接建立泛型陣列。但是,可以通過建立一個型別擦除的陣列,然後轉型的方式來建立泛型陣列。

public class GenericArrayDemo<T> {

    static class GenericArray<T> {
        private T[] array;

        public GenericArray(int num) {
            array = (T[]) new Object[num];
        }

        public void put(int index, T item) {
            array[index] = item;
        }

        public T get(int index) { return array[index]; }

        public T[] array() { return array; }
    }



    public static void main(String[] args) {
        GenericArray<Integer> genericArray = new GenericArray<Integer>(4);
        genericArray.put(0, 0);
        genericArray.put(1, 1);
        Object[] array = genericArray.array();
        System.out.println(Arrays.deepToString(array));
    }
}
// Output:
// [0, 1, null, null]
複製程式碼

擴充套件閱讀:www.cnblogs.com/jiangzhaowe…

我認為,對於泛型陣列的理解,點到為止即可。實際上,真的需要儲存泛型,還是使用容器更合適。

多維陣列

多維陣列可以看成是陣列的陣列,比如二維陣列就是一個特殊的一維陣列,其每一個元素都是一個一維陣列。

Java 可以支援二維陣列、三維陣列、四維陣列、五維陣列。。。

但是,以正常人的理解能力,一般也就最多能理解三維陣列。所以,請不要做反人類的事,去定義過多維度的陣列。

多維陣列使用示例:

public class MultiArrayDemo {
    public static void main(String[] args) {
        Integer[][] a1 = { // 自動裝箱
            {1, 2, 3,},
            {4, 5, 6,},
        };
        Double[][][] a2 = { // 自動裝箱
            { {1.1, 2.2}, {3.3, 4.4} },
            { {5.5, 6.6}, {7.7, 8.8} },
            { {9.9, 1.2}, {2.3, 3.4} },
        };
        String[][] a3 = {
            {"The", "Quick", "Sly", "Fox"},
            {"Jumped", "Over"},
            {"The", "Lazy", "Brown", "Dog", "and", "friend"},
        };
        System.out.println("a1: " + Arrays.deepToString(a1));
        System.out.println("a2: " + Arrays.deepToString(a2));
        System.out.println("a3: " + Arrays.deepToString(a3));
    }
}
// Output:
// a1: [[1, 2, 3], [4, 5, 6]]
// a2: [[[1.1, 2.2], [3.3, 4.4]], [[5.5, 6.6], [7.7, 8.8]], [[9.9, 1.2], [2.3, 3.4]]]
// a3: [[The, Quick, Sly, Fox], [Jumped, Over], [The, Lazy, Brown, Dog, and, friend]]
複製程式碼

Arrays 類

Java 中,提供了一個很有用的陣列工具類:Arrays。

它提供的主要操作有:

  • sort - 排序
  • binarySearch - 查詢
  • equals - 比較
  • fill - 填充
  • asList - 轉列表
  • hash - 雜湊
  • toString - 轉字串

擴充套件閱讀:juejin.im/post/5a6ade…

小結


深入理解 Java 陣列

參考資料

相關文章