1. 程式人生 > >System.arraycopy、Arrays.copyOf和ArrayList的toArray介紹

System.arraycopy、Arrays.copyOf和ArrayList的toArray介紹

   1. 先介紹System.arraycopy的用法。以下是官方的文件

arraycopy

public static void arraycopy(Object src,
                             int srcPos,
                             Object dest,
                             int destPos,
                             int length)
從指定源陣列中複製一個數組,複製從指定的位置開始,到目標陣列的指定位置結束。從 src 引用的源陣列到 dest
引用的目標陣列,陣列元件的一個子序列被複制下來。被複制的元件的編號等於 length 引數。源陣列中位置在 srcPossrcPos+length-1 之間的元件被分別複製到目標陣列中的 destPosdestPos+length-1 位置。

如果引數 srcdest 引用相同的陣列物件,則複製的執行過程就好像首先將 srcPossrcPos+length-1 位置的元件複製到一個帶有 length 元件的臨時陣列,然後再將此臨時陣列的內容複製到目標陣列的 destPosdestPos+length-1 位置一樣。

If 如果 destnull,則丟擲 NullPointerException

異常。

如果 srcnull, 則丟擲 NullPointerException 異常,並且不會修改目標陣列。

否則,只要下列任何情況為真,則丟擲 ArrayStoreException 異常並且不會修改目標陣列:

  • src 引數指的是非陣列物件。
  • dest 引數指的是非陣列物件。
  • src 引數和 dest 引數指的是那些其元件型別為不同基本型別的陣列。
  • src 引數指的是具有基本元件型別的陣列且 dest 引數指的是具有引用元件型別的陣列。
  • src 引數指的是具有引用元件型別的陣列且 dest 引數指的是具有基本元件型別的陣列。

否則,只要下列任何情況為真,則丟擲 IndexOutOfBoundsException

異常,並且不會修改目標陣列:

  • srcPos 引數為負。
  • destPos 引數為負。
  • length 引數為負。
  • srcPos+length 大於 src.length,即源陣列的長度。
  • destPos+length 大於 dest.length,即目標陣列的長度。

否則,如果源陣列中 srcPossrcPos+length-1 位置上的實際元件通過分配轉換並不能轉換成目標陣列的元件型別,則丟擲 ArrayStoreException 異常。在這種情況下,將 k 設定為比長度小的最小非負整數,這樣就無法將 src[srcPos+k] 轉換為目標陣列的元件型別;當丟擲異常時,從 srcPossrcPos+k-1 位置上的源陣列元件已經被複制到目標陣列中的 destPosdestPos+k-1 位置,而目標陣列中的其他位置不會被修改。(因為已經詳細說明過的那些限制,只能將此段落有效地應用於兩個陣列都有引用型別的元件型別的情況。)

引數:
src - 源陣列。
srcPos - 源陣列中的起始位置。
dest - 目標陣列。
destPos - 目標資料中的起始位置。
length - 要複製的陣列元素的數量。
丟擲:
- 如果因為型別不匹配而使得無法將 src 陣列中的元素儲存到 dest 陣列中。
- 如果 srcdestnull

那麼有個疑問,如果源陣列和目標陣列型別不一樣,可以通過System.arraycopy嗎?比如Object的陣列可以拷貝到String的陣列嗎?

    	String[] strArray=new String[5];
    	try{
    	Object[] objArray=new Object[]{"241",5,"sfa",6,"2sd"};
    	System.arraycopy(objArray, 0, strArray, 0, 2);
    	}catch(Exception e){
    		e.printStackTrace();
    		System.out.println(Arrays.toString(strArray));
    	}
    	//output:
//    	java.lang.ArrayStoreException
//    	at java.lang.System.arraycopy(Native Method)
//    	at arrays.AarrysTest.main(AarrysTest.java:34)
//    [241, null, null, null, null]
經過測試後發現,如果源陣列存放的物件可以轉化為目標陣列存放的物件,則可以copy,但是如果不一致,則會丟擲ArrayStoreException異常。再次測試程式碼
package arrays;

import java.util.Arrays;

class Base{
	public int i=0;
	@Override
	public String toString(){
		return "Base"+String.valueOf(i);
	}
}

class Second extends Base{
	@Override
	public String toString(){
		i=2;
		return "Second"+String.valueOf(i);
	}
}

class Thrid extends Second{
	@Override
	public String toString(){
		i=3;
		return "Thrid"+String.valueOf(i);
	}
}

public class AarrysTest {
    public static void main(String[]args){
    	//不同型別的陣列arraycopy
    	//目標陣列是原陣列基類
    	Thrid[] thridarray=new Thrid[2];
    	thridarray[0]=new Thrid();
    	thridarray[1]=new Thrid();
    	Second[] copyarry=new Second[2];
    	System.arraycopy(thridarray, 0, copyarry, 0, 2);
    	System.out.println(Arrays.toString(copyarry));
    	//原陣列是目標陣列基類
    	Base[] secondarray=new Base[2];
    	secondarray[0]=new Second();
    	secondarray[1]=new Second();
    	System.arraycopy(secondarray, 0, copyarry, 0, 2);
    	System.out.println(Arrays.toString(copyarry));
//    	output:
//    	[Thrid3, Thrid3]
//      [Second2, Second2]

    }
}
2.Arrays.copyOf

檢視Arrays.copyOf的原始碼發現,也是基於System.arraycopy實現的。通過過載實現了基本資料型別陣列的複製和引用型別陣列的複製。原始碼如下

   public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        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;
    }

通過傳入陣列的class,利用反射建立了copy陣列,然後使用System.arraycopy將原陣列複製到copy陣列並返回。原陣列的陣列型別和copy陣列的陣列型別可以不一樣,比如原陣列是Object[],返回的copy陣列可以使Integer[]。

基本資料型別陣列實現如下

public static byte[] copyOf(byte[] original, int newLength) {
        byte[] copy = new byte[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
3.最好講一下ArrayList的toArray

由於java泛型使用了擦除實現,所以ArrayList實際儲存的是Object的陣列,程式碼也不知道T(返現引數型別)的實際型別,那麼如何返回具體引數型別的陣列呢?

   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;
    }
可以看到,如果傳入的陣列length小於ArrayList的size,則會通過Arrays.copyOf新建一個傳入型別的陣列並返回。如果大於,則直接使用System.arraycopy將a的0-size-1的資料替換為ArrayList的資料。