原文地址:https://blog.csdn.net/w574951402/article/details/53246777
本文是對Arrays.asList()方法從源碼角度進行分析,解析使用中的一些困惑。
首先看Arrays.asList()的源碼
public static <T> List<T> asList(T... a) { return new ArrayList<T>(a); }
使用該方法可以將一個變長參數或者數組轉換成List
看似很簡單但實際使用起來卻會發現存在很多問題,看下面代碼來發現問題。
問題一:基本類型數組作為參數問題
public class ArraysAsListTest { public static void main(String[] args) { int[] a = {1,2,3}; Integer[] b = {1,2,3}; List listA = Arrays.asList(a); List listA1 = Arrays.asList(1,2,3); List listB = Arrays.asList(b); System.out.println(listA.size());//out:1 System.out.println(listA1.size());//out:3 System.out.println(listB.size());//out:3 } }
嗯?用int類型的數組作為參數為什么輸出size是1呢,使用Integer類型size就是3了呢。
再看源碼,asList接收的是一個泛型變長參數,而我們知道基本類型是不能泛型化的,就是說8種基本類型不能作為泛型參數,要想作為泛型參數就要使用其所對應的包裝類。
但是listA的Size為什么是1呢,這是因為listA傳遞的是一個int類型的數組,數組是一個對象,它是可以泛型化的,也就是說例子中是把一個int類型的數組作為了T的類型,所以轉換后在List中就只有一個類型為int數組的元素。后邊ListA1與ListB也就可以理解了,一個是進行了自動打包,一個是本來就是包裝類型。
我們可以打印下list中元素類型進行驗證
System.out.println("ListA元素類型:"+listA.get(0).getClass()); //out:ListA元素類型:class [I System.out.println("ListA元素:"+Arrays.toString((int[]) listA.get(0))); //ListA元素:[1, 2, 3] 該處是為了驗證list中元素 System.out.println("ListA1元素類型:"+listA1.get(0).getClass()); //out:ListA1元素類型:class java.lang.Integer System.out.println("ListB元素類型:"+listB.get(0).getClass()); //out:ListB元素類型:class java.lang.Integer
問題二:asList()方法返回對象使用add()方法拋出異常
public class ArraysAsListTest { public static void main(String[] args) { List<String> pets = Arrays.asList("cat","dog"); pets.add("what"); } } 異常: Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(Unknown Source) at java.util.AbstractList.add(Unknown Source) at ArraysAsListTest.main(ArraysAsListTest.java:9)
What?一個List執行add方法會拋出異常,難道add方法不是List的基本用法嗎。還是來研究下asList的實現吧
查看一下Arrays.asList中使用的ArrayList到底長啥樣?
原來Arrays的asList方法使用的ArrayList類是一個內部定義的類,而不是java.util.ArrayList類。看其部分源碼
public class Arrays { ....... 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; } ...... } }
這個靜態內部類,存儲數組元素的a變量是final類型的,由此判斷,這個靜態內部類是不能做任何內部元素的添加刪除操作的!就跟String類一樣,String對象存儲字符數組的變量也是有final修飾符的。因為一旦增加數組元素,這個數組容量已經定好的容器就無法裝載增加的元素了。
內部類里面並沒有add,remove方法,可以看下這個類繼承的AbstractList類里面對這些方法的實現
public 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(); } }
Ok!找到異常的來源了,我們使用asList得到的對象add、remove方法直接就是拋出異常
如果要對asList得到的對象使用add、remove方法可以使用如下解決辦法
List<String> pets = new ArrayList<String>(Arrays.asList("a", "b", "c"));
問題三:上代碼
public class ArraysAsListTest { public static void main(String[] args) {
String[] test = {"a","b","c","d","e"}; List<String> testList = Arrays.asList(test); System.out.println("list原始順序:"+testList); //洗牌打亂list中元素順序 使用Collections.shuffled方法 Collections.shuffle(testList, new Random(2)); System.out.println("list打亂后順序:"+testList); //list順序打亂后 原數組會發生神馬??? System.out.println("list打亂順序后數組內容:"+Arrays.toString(test)); /* * list原始順序:[a, b, c, d, e] list打亂后順序:[e, a, c, b, d] list打亂順序后數組內容:[e, a, c, b, d] */ } }
哦,NO!!!我只是改變list的順序,然而數組順序卻也發生了變化,很多時候這並不是我們想要的。
這時候我們就要意識到Arrays.asList()產生的list對象會使用底層數組作為其物理實現,只要執行操作修改這個list就會修改原來的數組。要想不改變原來數組,就要在另一個容器中創建一個副本,寫法如下
List<String> testList = new ArrayList<String>(Arrays.asList(test));