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
引數。源陣列中位置在srcPos
到srcPos+length-1
之間的元件被分別複製到目標陣列中的destPos
到destPos+length-1
位置。如果引數
src
和dest
引用相同的陣列物件,則複製的執行過程就好像首先將srcPos
到srcPos+length-1
位置的元件複製到一個帶有length
元件的臨時陣列,然後再將此臨時陣列的內容複製到目標陣列的destPos
到destPos+length-1
位置一樣。If 如果
dest
為null
,則丟擲NullPointerException
如果
src
為null
, 則丟擲NullPointerException
異常,並且不會修改目標陣列。否則,只要下列任何情況為真,則丟擲
ArrayStoreException
異常並且不會修改目標陣列:src
引數指的是非陣列物件。dest
引數指的是非陣列物件。src
引數和dest
引數指的是那些其元件型別為不同基本型別的陣列。src
引數指的是具有基本元件型別的陣列且dest
引數指的是具有引用元件型別的陣列。src
引數指的是具有引用元件型別的陣列且dest
引數指的是具有基本元件型別的陣列。
否則,只要下列任何情況為真,則丟擲
IndexOutOfBoundsException
srcPos
引數為負。destPos
引數為負。length
引數為負。srcPos+length
大於src.length
,即源陣列的長度。destPos+length
大於dest.length
,即目標陣列的長度。
否則,如果源陣列中
srcPos
到srcPos+length-1
位置上的實際元件通過分配轉換並不能轉換成目標陣列的元件型別,則丟擲ArrayStoreException
異常。在這種情況下,將 k 設定為比長度小的最小非負整數,這樣就無法將src[srcPos+
k]
轉換為目標陣列的元件型別;當丟擲異常時,從srcPos
到srcPos+
k-1
位置上的源陣列元件已經被複制到目標陣列中的destPos
到destPos+
k-1
位置,而目標陣列中的其他位置不會被修改。(因為已經詳細說明過的那些限制,只能將此段落有效地應用於兩個陣列都有引用型別的元件型別的情況。) -
- 引數:
src
- 源陣列。srcPos
- 源陣列中的起始位置。dest
- 目標陣列。destPos
- 目標資料中的起始位置。length
- 要複製的陣列元素的數量。- 丟擲:
- - 如果因為型別不匹配而使得無法將
src
陣列中的元素儲存到dest
陣列中。 - - 如果
src
或dest
為null
。那麼有個疑問,如果源陣列和目標陣列型別不一樣,可以通過System.arraycopy嗎?比如Object的陣列可以拷貝到String的陣列嗎?
經過測試後發現,如果源陣列存放的物件可以轉化為目標陣列存放的物件,則可以copy,但是如果不一致,則會丟擲ArrayStoreException異常。再次測試程式碼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]
2.Arrays.copyOfpackage 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] } }
檢視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[]。基本資料型別陣列實現如下
3.最好講一下ArrayList的toArraypublic 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; }
由於java泛型使用了擦除實現,所以ArrayList實際儲存的是Object的陣列,程式碼也不知道T(返現引數型別)的實際型別,那麼如何返回具體引數型別的陣列呢?
可以看到,如果傳入的陣列length小於ArrayList的size,則會通過Arrays.copyOf新建一個傳入型別的陣列並返回。如果大於,則直接使用System.arraycopy將a的0-size-1的資料替換為ArrayList的資料。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; }