List接口是Collection接口的一个子接口。
List接口的特点:
1.有序[存储有序]
2.数据可重复
3.可以存储null值
4.可以根据index查找相应的元素。
void add(int index,E element):在指定index位置处添加元素element.
boolean addAll(int index,Collection<? extends E>c):在指定index位置处插入指定集合的所有元素
E remove(int index):删除index位置上的元素
E set(int index,E element):修改index位置上的元素为element。
E get(int index):获取指定位置上的元素
int indexOf(Object o):从左往右查找,获取o的位置,若o不存在,返回-1。
int lastIndexOf(Object o):从右往左找,获取o的位置,若不存在,返回-1.
List<E> subList(int fromIndex,int toIndex):截取从fromIndex开始到toIndex-1处的元素,并返回一个新的List
ArrayList是List接口的实现类之一,ArrayList底层存储数据以数组存储的。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList继承AbstractList类,实现了List接口,可以被序列化,也可以被克隆,支持快速随机访问。
private static final long serialVersionUID = 8683452581122892189L; //版本号
private static final int DEFAULT_CAPACITY = 10;// 底层数组的默认容量
private static final Object[] EMPTY_ELEMENTDATA = {
}; //存储数据的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
}; // 存储数据的数组
transient Object[] elementData; //存储数据的数组
private int size; //元素个数
// 数组的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
我们可以看到,ArrayList的底层数组默认容量时10,但是我们看到了三个用于存储数据的的数组,其中两个被声明为空数组,一个还没有被初始化。这是什么原因呢?别着急,我们先来看一看ArrayList的构造函数:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
我们可以看到,ArrayList为我们提供了三种构造函数,分别是:无参构造函数、带一个int类型参数的构造函数,以及一个Collection类型参数的构造函数
当我们调用无参构造函数时,就会将我们的DEFACULT_EMPTY_ELEMENTDATA
赋值给element,即当我们调用了空参构造器时,会将element初始化成一个空数组
当我们调用带一个int类型参数的有参构造时,如果 initialCapacity>0
,则会按照我们给定的容量去初始化底层存储数据的数组。如果initialCapacity==0
,则会将EMPTY_ELEMENTDATA
赋给elementData。
当我们调用Collection参数类型的构造器时,会将指定的Collection集合中的元素拷贝到该ArrayList中,如果失败,则用EMTY_ELEMENTDATA赋值。
所以,通过构造函数我们就可以知道,DEFALUT_EMPTY_ELEMENTDATA和EMPTY_ELEMENTDATA
这两个数组就是为了区分调用的是哪一个构造器而设定的。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add()方法中调用了ensureCapacityInternal()方法,然后再向elementData中添加元素。
我们来看一看ensureCapacityInternal():
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static