在java.util.ArrayList源碼中:java
c.toArray might (incorrectly) not return Object[] (see 6260652) 產生疑惑:數組
附上Java Bug 網址: Java Bug Databasedom
,能夠根據關鍵詞或bug id 查詢詳細信息
這個Bug的描述中能夠看出:
緣由:Arrays內部實現的ArrayList的toArray()方法的行爲與規範不一致。
代碼測試:
import java.util.*; public class Test{ public static void demo1(){ System.out.println("this is demo1"); List<String> list=new ArrayList<>(); list.add("張三"); list.add("王五"); Object[] arr=list.toArray(); System.out.println(arr.getClass().getCanonicalName()); arr[0]=new Object(); Test.printArr(arr); /* 正常編譯、執行: this is demo1 java.lang.Object[] java.lang.Object@15db9742 王五 */ } public static void demo2(){ System.out.println("this is demo2"); List<String> list = Arrays.asList("張三", "王五"); Object[] arr=list.toArray(); System.out.println(arr.getClass().getCanonicalName()); arr[0]=new Object(); Test.printArr(arr); /* 正常編譯 執行輸出: this is demo2 java.lang.String[] Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object at Test.demo2(Test.java:31) at Test.main(Test.java:55) */ } public static void demo3() { System.out.println("this is demo3"); Object[] arr = new String[]{"張三", "王五"}; System.out.println(arr.getClass().getCanonicalName()); arr[0] = 7; Test.printArr(arr); /* 正常編譯 執行輸出: this is demo3 java.lang.String[] Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer at Test.demo3(Test.java:48) at Test.main(Test.java:71) */ } public static void printArr(Object[] arr) { for (Object o : arr) { System.out.print(o + " "); } System.out.println(); } public static void main(String[]args){ //Test.demo1(); //Test.demo2(); Test.demo3(); } }
輸出截圖:
分析過程詳解:
第一步:
看ArrayList帶Collection對象的構造函數源碼(java.util.ArrayList):
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }
看java.util.ArrayList,中toArray()源碼:
public Object[] toArray() { return Arrays.copyOf(elementData, size); } /** * 返回 ArrayList 元素組成的數組 * @param a 須要存儲 list 中元素的數組 * 若 a.length >= list.size,則將 list 中的元素按順序存入 a 中,而後 a[list.size] = null, a[list.size + 1] 及其後的元素依舊是 a 的元素 * 不然,將返回包含list 全部元素且數組長度等於 list 中元素個數的數組 * 注意:若 a 中原本存儲有元素,則 a 會被 list 的元素覆蓋,且 a[list.size] = null * @return * @throws ArrayStoreException 當 a.getClass() != list 中存儲元素的類型時 * @throws NullPointerException 當 a 爲 null 時 */ @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { // 若數組a的大小 < ArrayList的元素個數,則新建一個T[]數組, // 數組大小是"ArrayList的元素個數",並將「ArrayList」所有拷貝到新數組中 if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 若數組a的大小 >= ArrayList的元素個數,則將ArrayList的所有元素都拷貝到數組a中。 System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
能夠看出,因爲ArrayList中elementData類型爲Object[],因此調用copyOf()返回值類型爲Object[]。函數
第二步:測試
看 Arrays.asList()源碼:this
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
仔細閱讀官方文檔,你會發現對 asList 方法的描述中有這樣一句話:spa
返回一個由指定數組生成的固定大小的 List。3d
注意:參數類型是 T ,根據官方文檔的描述,T 是數組元素的 class。code
任何類型的對象都有一個 class 屬性,這個屬性表明了這個類型自己。原生數據類型,好比 int,short,long等,是沒有這個屬性的,具備 class 屬性的是它們所對應的包裝類 Integer,Short,Long。對象
asList 方法的參數必須是對象或者對象數組,而原生數據類型不是對象。當傳入一個原生數據類型數組時,asList 的真正獲得的參數就不是數組中的元素,而是數組對象自己。(解決方案:使用包裝類數組。)
繼續分析:
此時的ArrayList並不是咱們經常使用的java.util.ArrayList,而是Arrays的內部類。它繼承自AbstractList,天然實現了Collection接口,代碼以下:
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) { if (array==null) throw new NullPointerException(); a = array; } public int size() { return a.length; } 。。。。。。 }
ublic abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
。。。。。。。 public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } 。。。。。。
abstractList這個抽象類所定義的add和remove方法,僅僅是拋出了一個異常!
若是是想將一個數組轉化成一個列表並作增長刪除操做的話,建議代碼以下:
public class Test { public static void main(String[] args) { String[] myArray = { "張三", "李四", "趙六" }; List<String> myList = new ArrayList<String>(Arrays.asList(myArray)); myList.add("王五"); } };
demo2(測試代碼中的):
public static void demo2(){ System.out.println("this is demo2"); List<String> list = Arrays.asList("張三", "王五"); Object[] arr=list.toArray(); System.out.println(arr.getClass().getCanonicalName()); arr[0]=new Object(); Test.printArr(arr); /* 正常編譯 執行輸出: this is demo2 java.lang.String[] Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object at Test.demo2(Test.java:31) at Test.main(Test.java:55) */ }
上面的拋出異常分析:
asList方法直接將String[]數組做爲參數傳遞給ArrayList的構造方法,而後將String[]直接賦值給內部的a,因此a的真實類型是String[],根據JLS規範String[]的clone方法返回的也是String[]類型。最終,toArray()方法返回的真實類型是String[],此時,操做arr[0]=new Object();是向數組中添加Object對象,就會報異常的問題了。
Jdk 6260652 Bug 問題是在2005年提出的,如今已經解決了,使用toArray(T[] a)避免Exception的發生,因此可能會致使類型不匹配的錯誤。
小總結:
Arrays.asList()的使用方法:
該方法是將數組轉化爲list。有如下幾點須要注意:
1.該方法不適用於基本數據類型(byte,short,int,long,float,double,boolean)
解決方案:使用包裝類數組,例子以下:
public class Test { public static void main(String[] args) { Integer[] myArray = { 1, 2, 3 }; List myList = Arrays.asList(myArray); System.out.println(myList.size()); } }
2.該方法將數組與列表連接起來,當更新其中之一時,另外一個自動更新
3.不支持add和remove方法
將數組轉化爲一個List對象,通常會想到Arrays.asList()方法,這個方法會返回一個ArrayList類型的對象。可是用這個對象對列表進行添加刪除更新操做,就會報UnsupportedOperationException異常。
緣由:這個ArrayList類並不是java.util.ArrayList類,而是Arrays類的靜態內部類!
說明:asList的返回對象是一個Arrays內部類,並無實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換接口,後臺的數據還是數組。
String[] str = new String[]{"張三","王五"}; List list = Arrays.asList(str);
第一種狀況:list.add("趙四"); //運行時異常
第二種狀況:str[0] = "大二哈"; //list.get(0)也隨着修改。
此類包含用來操做數組(好比排序和搜索)的各類方法。此類還包含一個容許將數組做爲列表來查看的靜態工廠。 除非特別註明,不然若是指定數組引用爲 null,則此類中的方法都會拋出 NullPointerException。