數組和列表的轉換問題

Array 和 List 都是咱們在開發過程當中常見的數據結構。咱們都知道 Array 是定長的,List 是可變長。並且,List 的實現類 ArrayList 也是根據 Array 去實現的。java

如下 Array 指代數組,List 指代數組列表。

Array 轉 List

固然最原始的方法就是使用遍歷的方式,將 Array 中的元素都添加到 List 中。這種實現方式這裏不做贅述。數組

Java1.2 以後,Jdk 語言提供 Arrays 這個工具類。大大簡化了咱們常見的 Array 操做,可是也有很多須要注意的問題。數據結構

以下:函數

Integer[] a = { 1, 2 };
List<Integer> l = Arrays.asList(a);

這是咱們常見的 Array 轉換 List 的方式,可是這個使用上有一個問題。當對 List 對象 l 進行列表插入操做時:工具

l.add(3);

程序就會拋出異常 java.lang.UnsupportedOperationException。這是爲何呢?ui

查看 Arrays.asList 源碼發現,設計

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

這裏返回的 ArrayList 並非 java.util.ArrayList 而是 java.util.Arrays.ArrayList。Arrays 又新建了一個 ArrayList 內部類,實現了一些基本 get set 方法。code

回頭查看 java.util.Arrays.ArrayList.ArrayList(E[] array) 構造函數,對象

ArrayList(E[] array) {
    a = Objects.requireNonNull(array);
}

不難發現,java.util.Arrays.ArrayList 雖然打着 List 的旗號,繼承了 AbstractList 。可是其只是在 Array 的基礎上進行了簡單的封轉,AbstractList 中則是直接重寫了 add 方法,表示這個方法是不容許操做。blog

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

明白了這個錯誤產生的緣由,回頭想一下 Java 的這些開發者們爲何這樣設計。

ArrayList 中若是要添加一個元素,則須要先對其內部的 Array 進行擴容,而後將 Old Array 複製到擴容後的 New Array 中。若是 Array 轉 List 僅僅是讀取操做,或是在 Array 的 Size 範圍以內進行替換操做,再將 Array 複製一遍,難免會對內存進行浪費,倒不如直接將原始的 Array 直接拿來維護更爲直接和高效(正如java.util.Arrays.ArrayList的實現方式)。

明白這個原因以後,若是要在 Array 轉 List 以後,不僅有隻讀操做,那麼則須要下面的實現,

List<Integer> l = new ArrayList<>(Arrays.asList(a));

雖然在咱們平常的開發過程當中,已經習慣了使用 ArrayList 去代替 Array,可是瞭解此處 Java 的轉換過程仍是可以讓咱們少踩坑。

List 轉 Array

由於 Array 的長度不可變,因此這個轉換過程當中,會有長度不匹配的狀況。

常見的轉換方法是 ArrayList.toArray()ArrayList.toArray(T[])

這兩個實現的共同點是都是對 ArrayList 中的 Array 進行 Copy 操做,生成一個新的數組返回。不一樣點是前者返回值是 Object[],後者是 T[]

ArrayList.toArray(T[]) 的使用過程當中須要注意,當要轉換的 Array 長度小於 ArrayList 的 size 時,不要試圖經過傳入形參的方式進行轉換,雖然這在 Array 的長度大於 List 時不會出現問題。

以下代碼:

// l [1, 2, 3]
Integer[] a = new Integer[2];
l.toArray(a); // error 正確寫法:a = l.toArray(a);
Stream.of(a).forEach(System.out::println);

輸出結果是:null null

查看源碼實現:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

// Arrays.copyOf
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

可見,當 a.length < size 成立,入參 a 並無被使用,因此 a 依然是 new Integer[2]

因此,極度建議在轉換以前初始化 Array 的長度爲 ArrayList 的 size,而且使用返回值從新給 Array 賦值。

// l [1, 2, 3]
Integer[] b = new Integer[l.size()];
l.toArray(b);
Stream.of(b).forEach(System.out::println);

原文地址:https://xdbin.com/blog/8a9eec5167da2d4501681e8c3f180001

相關文章
相關標籤/搜索