前言
List列表接口及其实现类是Collection体系结构中的重要一环。List是有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。接口的定义:
public interface List<E>extends Collection<E>
正文
一,List接口概述
List接口解决插入元素的问题,List接口中的特有方法都是围绕着“索引”来定义的,并支持对元素的增删改查。
get(int index),返回指定位置的元素。
set(int index,E element),用指定元素替换指定位置的元素。
add(int index,E element),在指定位置插入指定元素。
remove(int index),删除指定位置的元素。与 set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。
List 接口提供了 4 种对列表元素进行定位(索引)访问方法。列表(像 Java 数组一样)是基于 0的。注意,这些操作可能在和某些实现(例如 LinkedList类)的索引值成比例的时间内执行。因此,如果调用者不知道实现,那么在列表元素上迭代通常优于用索引遍历列表。
通常,某个线程在Collection上进行迭代时,不允许另一个线程线性修改该Collection,如果检测到这种行为,一些Iterator可能会抛出ConcurrentModificationException异常。原因是在迭代过程中,对集合进行了修改,而迭代器并不知带集合中的变化。解决方法是,List接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。
二,ArrayList类
此类提供 List 接口的骨干实现,以最大限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作。ArrayList类底层维护一个数组,所以本身具有“随机访问快,增删慢”的特点。下面我们来看一下ArrayList的部分源码:
- ArrayList类名
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- ArrayList类成员变量,可见底层维护的是Object数组
private static final int DEFAULT_CAPACITY = 10; // ArrayList默认长度
private static final Object[] EMPTY_ELEMENTDATA = {}; // 空Object数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认的空Object数组
transient Object[] elementData; // 缓存数组,不可序列化
private int size; // List长度
- ArrayList类3个构造方法
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() { // 默认长度为10的构造方法
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) { // 参数为Collection子类对象的构造方法
elementData = c.toArray(); // 子类序列各自转为数组,赋值给缓存数组
if ((size = elementData.length) != 0) { // 缓存数组再进行判断,最后决定最终存什么。
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
- ArrayList常用方法之add()方法
public boolean add(E e) { // 在List末位添加元素
ensureCapacityInternal(size + 1); // 首先扩容
elementData[size++] = e; // 添加元素
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index); // 检查脚标是否越界
ensureCapacityInternal(size + 1); // 扩容
// 下面函数表示index处元素整体往后挪一位
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element; // 放入新元素
size++;
}
- ArrayList常用方法之add()方法的依赖方法
private void ensureCapacityInternal(int minCapacity) { // 计算所需最小空间,并调用开辟空间的方法
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 计算所需最小空间
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity); // 调用开辟空间的方法
}
private void ensureExplicitCapacity(int minCapacity) { // 开辟空间的方法
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity); // 数组扩容方法
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// Arrays.copyOf()最终调用native方法实现数组扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
- ArrayList获取迭代器的方法:iterator(),listIterator()
详见本人这篇博客中的迭代器详解部分:http://blog.csdn.net/qq_32166627/article/details/72683124
- ArrayList其他方法要么很简单,要么与add方法类似。不再一一列举。
总结
ArrayList是使用比较多的集合结构,其使用要牢牢掌握。