數組與List的互轉及原理分析

一:首先我們將數組轉爲List。

方法有兩種:

1、使用jdk提供的類:Arrays.asList() 

2、使用:Collections.addAll() 

我們先看第一種方法;

API裏的說法是這樣的。

public static <T> List<T> asList(T... a)
返回一個受指定數組支持的固定大小的列表。(對返回列表的更改會“直接寫”到數組。)此方法同 Collection.toArray() 一起,充當了基於數組的 API 與基於 collection 的 API 之間的橋樑。返回的列表是可序列化的,並且實現了 RandomAccess。 
此方法還提供了一個創建固定長度的列表的便捷方法,該列表被初始化爲包含多個元素: 
     List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
  


 

   使用asList得到的是一個固定程度的一個list,對新的list做元素的修改時可以的,但是,如果更改了大小就會報異常.

這有個很重要的東西,就是,使用asList()方法是可以得到List,但是如果不保證你的數組是對象類型的數組的話(除了基本數據類型的類型),轉換完成後你會發現,轉完後的list大小是1,而list[0] --> 指向的是你的數組,不止這一個方法,數組與list的互轉都存在這個問題。也就是你list的第一個元素是一個數組。在下面Collections方法裏有列出一個例子進行對比。

 

 

//數組轉集合
		//方法一:使用Arrays.asList(),但是要注意轉換後的list不能做大小和結構性更改
		Integer[] a = {2,5,7,8,9,6};
		List list = new ArrayList();
		list = Arrays.asList(a);//這個對轉換後的集合大小和結構不能更改
		list.add(6);//做增加操作更改數組大小
		System.out.println(list.get(0));

會報這樣一個異常:

先看看源碼:

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


從源碼來看,它放回了一個ArrayList,貌似沒毛病

如果你往下找的話,你會發現這樣一個東西:

 

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);//這裏就是最終找到地方,
        }
 public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

 

   發現我們傳入的數組在這裏就是array,然後返回給了a,而 a 在這個類裏被定義爲了final,只要有點基礎的小夥伴肯定知道被這個修飾的變量不能被改變,而引用類型的變量,其地址不能被改變,內容是可以被修改的,被修飾的數組,長度是不能改變的,內容是可以修改的。 所以這個方法返回的list是不能被修改大小的。

這樣做數據修改:list.set(2, 1);得到的結果就是:[2, 5, 1, 8, 9, 6],正常運行。而使用:list.add(6);和list.remove(2);就會報錯。這樣的使用的確是有很大限制,但對於不做大小方面修改的操作已經可以了。

 

      如果你抱怨道不行呀,還不夠我使用呀,僅僅只是變爲list,不能做list的操作那還有什麼意義。   我們還有另一種解決辦法:在轉換的同時把他加入到ArrayList中,這樣就可實現轉爲list後並能使用list的功能了。

//數組轉集合
		//方法一:使用Arrays.asList(T...a),但是要注意轉換後的list不能做大小和結構性更改
		Integer[] a = {2,5,7,8,9,6};
		List list = new ArrayList();
		//list = Arrays.asList(a);//這個對轉換後的集合大小和結構不能更改
		list = new ArrayList(Arrays.asList(a));
		list.add(6);//做增加操作更改數組大小
		System.out.println(list.toString());

結果:[2, 5, 7, 8, 9, 6, 6]
   第二種方法:使用Collections.addAll() 這個方法得到list不像上一個方法裏說的list,可以做list的各種操作。但有一點是相同的,如果你的數組是基本類型的,那麼第一個元素是一個數組。

//方法二:使用Collections.addAll()
		list = new ArrayList ();
		Collections.addAll(list, a);
		System.out.println(list);
		list.add(8);
		System.out.println(list);

數組是:Integer[] a = {2,5,7,8,9,6};

則結果是:

[2, 5, 7, 8, 9, 6]
[2, 5, 7, 8, 9, 6, 8]
數組是:int[] a = {2,5,7,8,9,6};

則結果是:
[[I@15db9742]
[[I@15db9742, 8]

懷疑的話,可以這樣加一句顯示:System.out.println(Arrays.toString((int[])list.get(0)));

顯示:[2, 5, 7, 8, 9, 6]

 

 

 

二、List 轉數組

使用集合裏的方法toArray()

還是先看API,有兩個方法可以實現

public Object[] toArray()
按適當順序(從第一個到最後一個元素)返回包含此列表中所有元素的數組。 
由於此列表不維護對返回數組的任何引用,,因而它將是“安全的”。(換句話說,此方法必須分配一個新的數組)。因此,調用者可以自由地修改返回的數組。 
public <T> T[] toArray(T[] a)
按適當順序(從第一個到最後一個元素)返回包含此列表中所有元素的數組;返回數組的運行時類型是指定數組的運行時類型。如果指定的數組能容納列表,則將該列表返回此處。否則,將分配一個具有指定數組的運行時類型和此列表大小的新數組。 
如果指定的數組能容納隊列,並有剩餘的空間(即數組的元素比隊列多),那麼會將數組中緊接 collection 尾部的元素設置爲 null。(僅 在調用者知道列表中不包含任何 null 元素時才能用此方法確定列表長度)。


同樣的是不建議使用基本數據類型轉換的。

第一個無參數的方法是返回Object[] ,不明所以的小夥伴們一開始就這樣寫:

Integer[] a2 = new Integer[list.size()];//開闢list大小的數組
		a2 = (Integer[]) list.toArray(a2);


然後莫名的被控制檯的ERROR嚇懵逼了,我剛開始就被嚇懵逼了。


   我們這裏要講講Object 和 Object[] 的關係了,我們都知道Object是所有類的超類,所有類都屬於Object,但是Object[]是用於存儲Object元素的數組,好像到這裏大家都不明白,那我們將Object看成一個對象,所有的對象都屬於Object這個對象,那麼,Object[]也看成一個對象,數組本身不屬於Object類,同理也沒有一個對象屬於它,所以Object[]這個對象跟Object沒有關係。大家懂了之後我們再來看代碼:Integer[] a2 通過list.toArray()返回一個Object[]的對象,而我們用Integer[]對象來接收,這兩者沒有關係,所以不能相互轉化,所以才報的異常。

 

現在再來看看源碼:

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

 

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;
    }

1、先看第一個方法:toArray(),返回爲一個Object[]的對象,Arrays.copyOf( )

public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

在往下看底層的實現:

 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;
    }

   三目運算符哪裏:將我們傳入的數組對象(Object)newType和(Object)Object[]這個對象比較,是否相等,相等則返回new Object[newLength]是一個new出來的新的數組,不等則會進入Array.newInstance,再往下走會發現拋出了一個異常。所以必須要準備一數組容器來接收,而且必須是一個Object[]類型的,所以我們使用第一個方法的話會得到一個Object[]數組,想要使用,就有要將數組裏的元素遍歷在強制轉換爲我們的類型,這個相對來說麻煩了些,實現如下:

for(int i=0 ; i < aa.length ; i++) {
					aa[i] = ((Integer)aa[i]).intValue();
				}


2、第二個方法:toArray(T...a)

參數類型是可變參數,就是參數個數不確定,可放任意個,底層實現是數組實現的,有興趣的小夥伴可深入瞭解。

源碼:

 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;
    }

   這個不難看出當a.length < size時,return,返回的是第一個方法的底層實現,就不提了;然後使用系統提供的數組複製system.arraycopy(原數組,原始數組開始索引,目的數組,目的數組開始索引,大小),這裏目的數組是 a (也就是我們傳入的數組),然後到後面返回的 是 數組 a ,這時候 a 已經是複製好的數組了。所以我們使用時也可以不用再準備容器去接收它, 

 

				Integer[] a2 = new Integer[list.size()];//開闢list大小的數組
				Object[] aa = new Object[list.size()];
				list.toArray(a2);
				System.out.println("+"+Arrays.toString(a2));

結果:+[2, 5, 7, 8, 9, 6]

 

總結:我們介紹了數組轉list的兩種方法:第一是asList:list = Arrays.asList(a)  list = new ArrayList(Arrays.asList(a));兩種的使用及區別,一個是固定長度,不可改變,不然會報異常,一個是擁有list的所以東西可增可刪;第二個是Collections.addAll(),有它有兩個參數,第一個是集合對象,第二個是數組對象,使用後內部實現轉換,簡單方便,然後是list轉數組:toArray(),重載了兩種,一個是無參返回Object[] ,一個是有參返回所以類型數組,第一個轉換成指定類型數組比較麻煩不太好用,第二個簡單方便好用。


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章