java 源码分析Arrays.asList方法详解
最近,抽空把javaArrays工具类的asList方法做了源码分析,在网上整理了相关资料,记录下来,希望也能帮助读者!
Arrays工具类提供了一个方法asList,使用该方法可以将一个变长参数或者数组转换成List。
其源代码如下:
/** *Returnsafixed-sizelistbackedbythespecifiedarray.(Changesto *thereturnedlist"writethrough"tothearray.)Thismethodacts *asbridgebetweenarray-basedandcollection-basedAPIs,in *combinationwith{@linkCollection#toArray}.Thereturnedlistis *serializableandimplements{@linkRandomAccess}. * *<p>Thismethodalsoprovidesaconvenientwaytocreateafixed-size *listinitializedtocontainseveralelements: *<pre> *List<String>stooges=Arrays.asList("Larry","Moe","Curly"); *</pre> * *@paramathearraybywhichthelistwillbebacked *@returnalistviewofthespecifiedarray */ @SafeVarargs publicstatic<T>List<T>asList(T...a){ returnnewArrayList<>(a); }
问题发现
根据上述方法的描述,我们先来编写几个例子:
/** *@authorwangmengjun * */ publicclassArrayExample{ publicstaticvoidmain(String[]args){ /**使用变长参数*/ List<String>array1=Arrays.asList("Welcome","to","Java","world"); System.out.println(array1); /**使用数组*/ List<String>array2=Arrays.asList(newString[]{"Welcome","to","Java","world"}); System.out.println(array2); } }
运行上述程序,输出如下内容。
[Welcome,to,Java,world]
[Welcome,to,Java,world]
心血来潮,突然想在创建的列表中添加一个字符串“Cool~~~”, 走一个。
/**使用变长参数*/ List<String>array1=Arrays.asList("Welcome","to","Java","world"); array1.add("Cool~~~");
结果,遇到一个UnsupportedOperationException异常:
Exceptioninthread"main"java.lang.UnsupportedOperationException atjava.util.AbstractList.add(UnknownSource) atjava.util.AbstractList.add(UnknownSource) attest.ArrayExample.main(ArrayExample.java:36)
不可思议,newArrayList<>(a)产生的列表调用add方法,竟然遇到问题。
原因查找
那么问题来了,到底发生了什么事情?带着疑问,去查看一下Arrays.asList中使用的ArrayList到底长啥样?
原来Arrays的asList方法使用的ArrayList类是一个内部定义的类,而不是java.util.ArrayList类。
其源代码如下:
/** *@serialinclude */ privatestaticclassArrayList<E>extendsAbstractList<E> implementsRandomAccess,java.io.Serializable { privatestaticfinallongserialVersionUID=-2764017481108945198L; privatefinalE[]a; ArrayList(E[]array){ if(array==null) thrownewNullPointerException(); a=array; } publicintsize(){ returna.length; } publicObject[]toArray(){ returna.clone(); } public<T>T[]toArray(T[]a){ intsize=size(); if(a.length<size) returnArrays.copyOf(this.a,size, (Class<?extendsT[]>)a.getClass()); System.arraycopy(this.a,0,a,0,size); if(a.length>size) a[size]=null; returna; } publicEget(intindex){ returna[index]; } publicEset(intindex,Eelement){ EoldValue=a[index]; a[index]=element; returnoldValue; } publicintindexOf(Objecto){ if(o==null){ for(inti=0;i<a.length;i++) if(a[i]==null) returni; }else{ for(inti=0;i<a.length;i++) if(o.equals(a[i])) returni; } return-1; } publicbooleancontains(Objecto){ returnindexOf(o)!=-1; } }
从这个内部类ArrayList的实现可以看出,它继承了抽象类java.util.AbstractList<E>,但是没有重写add和remove方法,没有给出具体的实现。
但是,默认情况下,java.util.AbstractList类在add、set以及remove方法中,直接会抛出UnsupportedOperationException异常。AbstractList的部分源代码如下:
publicabstractclassAbstractList<E>extendsAbstractCollection<E>implementsList<E>{ /** *Soleconstructor.(Forinvocationbysubclassconstructors,typically *implicit.) */ protectedAbstractList(){ } publicEset(intindex,Eelement){ thrownewUnsupportedOperationException(); } /** *{@inheritDoc} * *<p>Thisimplementationalwaysthrowsan *{@codeUnsupportedOperationException}. * *@throwsUnsupportedOperationException{@inheritDoc} *@throwsClassCastException{@inheritDoc} *@throwsNullPointerException{@inheritDoc} *@throwsIllegalArgumentException{@inheritDoc} *@throwsIndexOutOfBoundsException{@inheritDoc} */ publicvoidadd(intindex,Eelement){ thrownewUnsupportedOperationException(); } /** *{@inheritDoc} * *<p>Thisimplementationalwaysthrowsan *{@codeUnsupportedOperationException}. * *@throwsUnsupportedOperationException{@inheritDoc} *@throwsIndexOutOfBoundsException{@inheritDoc} */ publicEremove(intindex){ thrownewUnsupportedOperationException(); } }
正是因为java.util.Arrays类的内部类ArrayList没有重写add和remove方法,所以,当我们调用其add方法时,其实就是调用了AbstractList类的add方法,结果就是直接抛出UnsupportedOperationException异常。
同理,在调用remove方法,或者调用与add、remove方法相关联的其它方法(如addAll)同样会遇到UnsupportedOperationException异常。
addAll的例子:
/** *@authorwangmengjun * */ publicclassArrayExample{ publicstaticvoidmain(String[]args){ /**使用变长参数*/ List<String>array1=Arrays.asList("Welcome","to","Java","world"); array1.addAll(Arrays.asList("AAA","BBB")); } }
Exceptioninthread"main"java.lang.UnsupportedOperationException atjava.util.AbstractList.add(UnknownSource) atjava.util.AbstractList.add(UnknownSource) atjava.util.AbstractCollection.addAll(UnknownSource) attest.ArrayExample.main(ArrayExample.java:36)
set的例子:
/** *@authorwangmengjun * */ publicclassArrayExample{ publicstaticvoidmain(String[]args){ /**使用变长参数*/ List<String>array1=Arrays.asList("Welcome","to","Java","world"); System.out.println(array1); //将Java替换成hello array1.set(2,"hello"); System.out.println(array1); } }
正是由于Arrays的内部类ArrayList重写了set方法,所以上述程序能够正常运行,不会再抛出UnsupportedOperationException异常。
结果如下:
[Welcome,to,Java,world]
[Welcome,to,hello,world]
使用场景
从上述的例子和简单分析来看,Arrays工具类提供了一个方法asList,使用该方法可以将一个变长参数或者数组转换成List。
但是,生成的List的长度是固定的;能够进行修改操作(比如,修改某个位置的元素);不能执行影响长度的操作(如add、remove等操作)。会抛出UnsupportedOperationException异常。
Arrays.asList比较适合那些已经有数组数据或者一些元素,而需要快速构建一个List,只用于读取操作,而不进行添加或删除操作的场景。
如果,想要根据已知数组数据,快速获取一个可进行增删改查的列表List,一个比较简单的方法如下:
重新使用java.util.ArrayList包装一层。
/** *@authorwangmengjun * */ publicclassArrayExample{ publicstaticvoidmain(String[]args){ /**使用变长参数*/ List<String>array1=newArrayList<>(Arrays.asList("Welcome","to","Java","world")); System.out.println(array1); array1.add("Cool~~~"); System.out.println(array1); } }
结果如下:
[Welcome,to,Java,world]
[Welcome,to,Java,world,Cool~~~]
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!