1.基本简介
- ArrayList是一个数组队列,相当于动态数组,与java中的数组相比,它的容量能动态增长,它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。 - ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的,在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
- ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
- ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
- 和Vector不同,ArrayList中的操作线程不安全的。所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
排序
Collections.sort(list);
list.sort(null);
list转为数组的方法
String[] arry1 = (String[])list.toArray();
String[] arry2 = list.toArray(new String[0]);
2.ArrayList源码分析
(1)私有属性仅两个
private transient Object[] elementData;
private int size;
(2)构造方法
//ArrayList带容量大小的构造函数
public ArrayList(int initialCapacity){
super();
if(initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity:"+ initialCapacity);
//新建一个数组
this.elementData = new Object[initialCapacity];
}
//ArrayList无参构造函数,默认容量是10
public ArrayList(){
this(10);
}
//创建一个包含collection的ArrayList
public ArrayList(Collection<? extends E> c ){
elementData = c.toArray();
size = elementData.length;
if(elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData,size,Object[].class);
}
(3)元素存储
//用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素
public E set(int index,E element){
RangeCheck(index);
E oldValue = (E)elementData[index];
elementData[index] = element;
return oldValue;
}
//将指定的元素添加到此列表的尾部
public boolean add(E e){
ensureCapacity(size + 1);
elementData[size++] = e;
return true;
}
//将指定的元素插入此列表中的指定位置
public void add(int index,E element){
if(index > size || index < 0)
throws new IndexOutOfBoundsException("Index:"+index+",Size:"+size);
ensureCapacity(size+1);
System.arraycopy(elementData,index,elementData,index+1,size-index);
elementData[index]=element;
size++;
}
//按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部
public boolean addAll(Collection<? extends E> c ){
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew);
System.arraycopy(a,0,elementData,size,numNew);
size += numNew;
return numNew != 0;
}
//从指定位置开始,将指定collection中的所有元素插入到此列表中
public boolean addAll(int index,Collection<? extends E> c){
if(index > size || inedx < 0)
throws new IndexOutOfBoundsException("Index:"+index+",Size:"+size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); //Increments modCount
int numMoved = size - index;
if(numMoved > 0)
System.arraycopy(elementData, index,elementData,index + numNew,numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
(4)元素读取
//返回此列表指定位置上的值
public E get(int index){
RangeCheck(index);
return (E) elementData[index];
}
(5)元素删除
remove(int index);
//移除此列表中指定位置上的元素
public E remove(int index){
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index -1;
if(numMoved > 0)
System.arraycopy(elementData,index+1,elementData,index,numMoved);
elementData[--size] = null; //let gc do its work
return oldValue;
}
remove(Object o);
//移除列表中首次出现的指定元素(如果存在),这是因为ArrayList允许存放重复的元素
public boolean remove(Object o){
//由于ArrayList中允许存放null,因此分两种情况处理
if(o == null){
for(int index = 0;index < size; index ++)
if(elementData[index] == null){
fastRemove(index);
return true;
}
}eles{
for(int index = 0;index < size; index++)
if(o.equals(elementData[index])){
fastRemove(index);
return true;
}
}
return false;
}
fastRemove(int index);
private void fastRemove(int index){
modCount ++;
int numMoved = size -index -1;
if(numMoved > 0)
System.arraycopy(elementData,index+1,elementData,index,numMoved);
elementData[--size] = null; //let gc do its work
}
(6)调整数组容量
public void ensureCapacity(int minCapacity){
modCount ++;
int oldCapacity = elementData.length;
if(minCapacity > oldCapacity){
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1 ;
if(newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elemenData,newCapacity);
}
}
Object oldData[] = elementData;//为什么要用到oldData[]
乍一看来后面并没有用到关于oldData, 这句话显得多此一举!
但是这是一个牵涉到内存管理的类, 所以要了解内部的问题。 而且为什么这一句还在if的内部,这跟elementData = Arrays.copyOf(elementData, newCapacity); 这句是有关系的
下面这句Arrays.copyOf的实现时新创建了newCapacity大小的内存,然后把老的elementData放入。好像也没有用到oldData,有什么问题呢。
问题就在于旧的内存的引用是elementData, elementData指向了新的内存块,如果有一个局部变量oldData变量引用旧的内存块的话,在copy的过程中就会比较安全,因为这样证明这块老的内存依然有引用,分配内存的时候就不会被侵占掉,然后copy完成后这个局部变量的生命期也过去了,然后释放才是安全的。
不然在copy的的时候万一新的内存或其他线程的分配内存侵占了这块老的内存,而copy还没有结束,这将是个严重的事情。
(7)提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能
public void trimToSize(){
modCount ++;
int oldCapacity = elementData.length;
if(size < oldCapacity){
elementData = Arrays.copyOf(elementData,size);
}
}
(8)Fail-Fast机制
- ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
总结
-
1、注意其三个不同的构造方法。无参构造方法构造的ArrayList的容量默认为10,带有Collection参数的构造方法,将Collection转化为数组赋给ArrayList的实现数组elementData。
-
2、注意扩充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量),而后用Arrays.copyof()方法将元素拷贝到新的数组。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList。
-
3、ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法。
-
4、ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
-
5、在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理,ArrayList中允许元素为null。