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~~~]
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!