Java中ArrayList类的用法与源码完全解析
System.Collections.ArrayList类是一个特殊的数组。通过添加和删除元素,就可以动态改变数组的长度。
一.优点
1.支持自动改变大小的功能
2.可以灵活的插入元素
3.可以灵活的删除元素
二.局限性
跟一般的数组比起来,速度上差些
三.添加元素
1.publicvirtualintAdd(objectvalue);
将对象添加到ArrayList的结尾处
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
内容为
abcde
2.publicvirtualvoidInsert(intindex,objectvalue);
将元素插入ArrayList的指定索引处
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
aList.Insert(0,"aa");
结果为
aaabcde
3.publicvirtualvoidInsertRange(intindex,ICollectionc);
将集合中的某个元素插入ArrayList的指定索引处
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
ArrayListlist2=newArrayList();
list2.Add("tt");
list2.Add("ttt");
aList.InsertRange(2,list2);
结果为
abtttttcde
四.删除
1.publicvirtualvoidRemove(objectobj);
从ArrayList中移除特定对象的第一个匹配项,注意是第一个
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
aList.Remove("a");
结果为
bcde
2.publicvirtualvoidRemoveAt(intindex);
移除ArrayList的指定索引处的元素
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
aList.RemoveAt(0);
结果为
bcde
3.publicvirtualvoidRemoveRange(intindex,intcount);
从ArrayList中移除一定范围的元素。Index表示索引,count表示从索引处开始的数目
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
aList.RemoveRange(1,3);
结果为
ae
4.publicvirtualvoidClear();
从ArrayList中移除所有元素。
五.排序
1. publicvirtualvoidSort();
对ArrayList或它的一部分中的元素进行排序。
ArrayListaList=newArrayList();
aList.Add("e");
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
DropDownList1.DataSource=aList;//DropDownListDropDownList1;
DropDownList1.DataBind();
结果为
eabcd
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
aList.Sort();//排序
DropDownList1.DataSource=aList;//DropDownListDropDownList1;
DropDownList1.DataBind();
结果为
abcde
2.publicvirtualvoidReverse();
将ArrayList或它的一部分中元素的顺序反转。
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
aList.Reverse();//反转
DropDownList1.DataSource=aList;//DropDownListDropDownList1;
DropDownList1.DataBind();
结果为
edcba
六.查找
1.publicvirtualintIndexOf(object);
2. publicvirtualintIndexOf(object,int);
3. publicvirtualintIndexOf(object,int,int);
返回ArrayList或它的一部分中某个值的第一个匹配项的从零开始的索引。没找到返回-1。
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");
intnIndex=aList.IndexOf(“a”);//1
nIndex=aList.IndexOf(“p”);//没找到,-1
4.publicvirtualintLastIndexOf(object);
5.publicvirtualintLastIndexOf(object,int);
6.publicvirtualintLastIndexOf(object,int,int);
返回ArrayList或它的一部分中某个值的最后一个匹配项的从零开始的索引。
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("a");//同0
aList.Add("d");
aList.Add("e");
intnIndex=aList.LastIndexOf("a");//值为2而不是0
7. publicvirtualboolContains(objectitem);
确定某个元素是否在ArrayList中。包含返回true,否则返回false
七.其他
1.publicvirtualintCapacity{get;set;}
获取或设置ArrayList可包含的元素数。
2.publicvirtualintCount{get;}
获取ArrayList中实际包含的元素数。
Capacity是ArrayList可以存储的元素数。Count是ArrayList中实际包含的元素数。Capacity总是大于或等于Count。如果在添加元素时,Count超过Capacity,则该列表的容量会通过自动重新分配内部数组加倍。
如果Capacity的值显式设置,则内部数组也需要重新分配以容纳指定的容量。如果Capacity被显式设置为0,则公共语言运行库将其设置为默认容量。默认容量为16。
在调用Clear后,Count为0,而此时Capacity切是默认容量16,而不是0
3.publicvirtualvoidTrimToSize();
将容量设置为ArrayList中元素的实际数量。
如果不向列表中添加新元素,则此方法可用于最小化列表的内存系统开销。
若要完全清除列表中的所有元素,请在调用TrimToSize之前调用Clear方法。截去空ArrayList会将ArrayList的容量设置为默认容量,而不是零。
ArrayListaList=newArrayList();
aList.Add("a");
aList.Add("b");
aList.Add("c");
aList.Add("d");
aList.Add("e");//Count=5,Capacity=16,
aList.TrimToSize();//Count=Capacity=5;
八.源码分析
List接口的一个实现类,内部是用一个数组存储元素值,相当于一个可变大小的数组。
1.签名
publicclassArrayList<E> extendsAbstractList<E> implementsList<E>,RandomAccess,Cloneable,Serializable
可以看到ArrayList继承了AbstractList抽象类,它实现了List接口的大多数方法。如果要实现一个不可变的List,只要继承这个类并且实现get(int)和size方法。如果要实现可变的List,需要覆盖set(int,E)。另外,如果List的大小是可变的,还要覆盖add(int,E)和remove()方法。
2.构造器
ArrayList提供了三个构造器:
ArrayList() ArrayList(Collection<?extendsE>c) ArrayList(intinitialCapacity)
Collection接口约定,每个集合类应该提供两个”标准”构造器,一个是无参数的构造器(上面第一个),另外一个是拥有单个参数(类型为Collettion)的构造器(上面第二个)。ArrayList还提供了第三个构造器,其接受一个int值,用于设置ArrayLi的初始大小(默认大小为10)。
3.相关方法
trimToSize
publicvoidtrimToSize(){
modCount++;
intoldCapacity=elementData.length;
if(size<oldCapacity){
elementData=Arrays.copyOf(elementData,size);
}
}
用于把ArrayList的容量缩减到当前实际大小,减少存储容量。其中的变量modCount由AbstracList继承而来,记录List发生结构化修改(structurallymodified)的次数。elementData中实际存储了ArrayList的元素,在ArrayList中声明为:privatetransientObject[]elementData;变量size是ArrayList的元素数量,当size<oldCapacity时,调用Arrays.copyOf方法实现缩减。
4.indexOf和lasIndexOf
publicintindexOf(Objecto){
if(o==null){
for(inti=0;i<size;i++)
if(elementData[i]==null)
returni;
}else{
for(inti=0;i<size;i++)
if(o.equals(elementData[i]))
returni;
}
return-1;
}
这两个方法返回指定元素的下标,要区分参数是否为null。lastIndexOf和indexOf类似,只不过是从后往前搜索。
5.ensureCapacity
publicvoidensureCapacity(intminCapacity){
if(minCapacity>0)
ensureCapacityInternal(minCapacity);
}
privatevoidensureCapacityInternal(intminCapacity){
modCount++;
//overflow-consciouscode
if(minCapacity-elementData.length>0)
grow(minCapacity);
}
privatevoidgrow(intminCapacity){
//overflow-consciouscode
intoldCapacity=elementData.length;
intnewCapacity=oldCapacity+(oldCapacity>>1);
if(newCapacity-minCapacity<0)
newCapacity=minCapacity;
if(newCapacity-MAX_ARRAY_SIZE>0)
newCapacity=hugeCapacity(minCapacity);
//minCapacityisusuallyclosetosize,sothisisawin:
elementData=Arrays.copyOf(elementData,newCapacity);
}
这个方法可以确保ArrayList的大小
6.add和addAll
publicvoidadd(intindex,Eelement){
rangeCheckForAdd(index);
ensureCapacityInternal(size+1);//IncrementsmodCount!!
System.arraycopy(elementData,index,elementData,index+1,
size-index);
elementData[index]=element;
size++;
}
add(intindex,Eelement)向指定位置添加元素,首先调用rangeCheckForAdd检查index是否有效,如果index>size||index<0将抛出异常。然后确保容量加1,调用System.arraycopy把从index开始的元素往后移动一个位置。最后把index处的值设置为添加的元素。还有一个重载的add(Ee)方法是直接把元素添加到末尾。
addAll(Collection<?extendsE>c)和addAll(intindex,Collection<?extendsE>c)则分别是向末尾和指定位置添加Collection中的所有元素。
7.remove和removeAll
publicbooleanremove(Objecto){
if(o==null){
for(intindex=0;index<size;index++)
if(elementData[index]==null){
fastRemove(index);
returntrue;
}
}else{
for(intindex=0;index<size;index++)
if(o.equals(elementData[index])){
fastRemove(index);
returntrue;
}
}
returnfalse;
}
remove(Objecto)方法删除指定的元素。首先是查找元素位置,然后调用fastRemove(index)删除,其代码如下:
privatevoidfastRemove(intindex){
modCount++;
intnumMoved=size-index-1;
if(numMoved>0)
//把index+1往后的元素都往前移动一个位置
System.arraycopy(elementData,index+1,elementData,index,
numMoved);
elementData[--size]=null;//Letgcdoitswork
}
重载的remove(intindex)方法用于删除指定位置的元素。removeRange(intfromIndex,inttoIndex)用于删除指定位置之间的所有元素。
removeAll(Collection<?>c)和retainAll(Collection<?>c)代码如下:
publicbooleanremoveAll(Collection<?>c){
Objects.requireNonNull(c);
returnbatchRemove(c,false);
}
publicbooleanretainAll(Collection<?>c){
Objects.requireNonNull(c);
returnbatchRemove(c,true);
}
它们都是通过调用batchRemove方法实现的,其代码如下:
privatebooleanbatchRemove(Collection<?>c,booleancomplement){
finalObject[]elementData=this.elementData;
intr=0,w=0;
booleanmodified=false;
try{
for(;r<size;r++)
if(c.contains(elementData[r])==complement)
elementData[w++]=elementData[r];
}finally{
//PreservebehavioralcompatibilitywithAbstractCollection,
//evenifc.contains()throws.
if(r!=size){
System.arraycopy(elementData,r,
elementData,w,
size-r);
w+=size-r;
}
if(w!=size){
//cleartoletGCdoitswork
for(inti=w;i<size;i++)
elementData[i]=null;
modCount+=size-w;
size=w;
modified=true;
}
}
returnmodified;
}
这个方法有两个参数,第一个是操作的Collection,第二个是一个布尔值,通过设置为true或false来选择是removeAll还是retainAll。try里面的语句是把留下来的放在0到w之间,然后在finally中第二个if处理w之后的空间,第一个是在c.contains()抛出异常时执行。