1. List 接口
继承 Collection 接口的一个接口,用来表示传统的线性表的结构,实现该接口的结构存储的数据不唯一(值不唯一,类型是唯一的),数据按照添加的顺序进行排列。
其主要扩展 Collection 的方法有:
public interface List<E> extends Collection<E> {
// 元素进行排序
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
// 获取某个下标的元素
E get(int index);
// 修改某个下标的元素
E set(int index, E element);
// 判断元素是否存在
// 从头开始,并返回第一相同元素的索引
int indexOf(Object o);
// 从后面开始,并返回第一个相同元素的索引
int lastIndexOf(Object o);
// 快速创建List对象的静态方法
static <E> List<E> of() {
return (List<E>) ImmutableCollections.ListN.EMPTY_LIST;
}
static <E> List<E> of(E e1) {
return new ImmutableCollections.List12<>(e1);
}
// 还有好几个重载的方法
...
}
2. 实现类
2.1 Vector
底层用数组存储,里面的方法都是线程安全的,现版本基本被抛弃了,锁的粒度比较大,性能低,不建议使用。
- 类的定义及关键字段:
- 用数组来保存数据。
- 默认数组元素初始化大小为10。
- capacityIncrement 当为0的时候默认扩大一倍的大小。
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 1. 重要的属性
// 用数组结构来保存数据
protected Object[] elementData;
// 数组中元素的个数
protected int elementCount;
// 扩容时数组增长的大小,如果该值小于等于0时会默认增长一倍
protected int capacityIncrement;
//-----------------------------------------------------
// 2.构造函数
public Vector() {
// 无参构造默认数组初始化大小为10
this(10);
}
public Vector(int initialCapacity) {
// 默认扩容因子为0,因此在扩容的时候会增长数组长度的一倍
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
// 最终都会调用这个
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
}
- 添加元素
- Add 方法并不会直接暴露给外部调用,会实际修改到数组结构。
- insertElementAt 方法被synchronized 关键字修饰,同步操作。
// 添加元素,修改到底层数组结构内容的有如下两个方法:
// 私有的,供其他 add 方法调用,往s的位置添加
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
// 超出底层数组大小需要扩容
elementData = grow();
elementData[s] = e;
elementCount = s + 1;
}
// 插入操作,往index位置插入,同步操作
public synchronized void insertElementAt(E obj, int index) {
if (index > elementCount) {
// 插入范围在0~elementCount
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
// 父类AbstractList中声明的用来记录结构被修改的次数,可以用于增强for循环中删除数组元素报错的情况。
modCount++;
final int s = elementCount;
Object[] elementData = this.elementData;
if (s == elementData.length)
elementData = grow();
// 将index开始的元素移动到 index+1 后,使用native方法,性能更高
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = obj;
elementCount = s + 1;
}
- 获取元素
// 获取元素
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
- 删除元素
// 移除元素
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
// 要移动元素的数量,如果是index为最后一个元素则不用移除操作
int numMoved = elementCount - index - 1;
if (numMoved > 0)
// 将index+1的位置往前移动即可删除
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
// 移除所有元素
public void clear() {
removeAllElements();
}
public synchronized void removeAllElements() {
final Object[] es = elementData;
for (int to = elementCount, i = elementCount = 0; i < to; i++)
es[i] = null;
modCount++;
}
- 扩容操作
// 扩容操作
private Object[] grow() {
// 至少扩容大于1个元素
return grow(elementCount + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
// 获取新的数组长度,当扩容因子为0的时候扩展为原来的1倍大小
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, capacityIncrement > 0 ? capacityIncrement : oldCapacity);
return elementData = Arrays.copyOf(elementData, newCapacity);
}
2.2 ArrayList
底层用数组存储,与Vector相比,ArrayList 的操作不是线程安全的,其他基本与Vector相同。
- 类的定义及关键字段
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 默认初始化数组元素的大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 创建没有元素的 ArrayList 的话所有 ArrayList 共享这个对象,节约空间
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 没有指定初始化容量时使用,可以用于标识在添加第一个元素的时候需要扩容的大小
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 底层数组结构,通过上面两个全局静态的常量可以发现,当初始化容量为0或者不指定的时候这是一个延迟创建的对象,知道真正添加元素的时候才创建
*/
transient Object[] elementData;
/**
* 元素个数
*/
private int size;
// 构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 容量为0的时候不创建对象
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 不指定初始化容量的时候在第一次添加元素时会初始化10个元素大小的数组
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
}
其他操作和 Vector 差不多,主要是扩容机制不太一样,ArrayList 没有扩容因子,其在一般情况都是扩容当前长度的 1/2 的大小。
有个特殊的情况就是如果当初始化容量为0的时候第一次添加元素的时候只会扩容一个元素。
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, oldCapacity >> 1);// 右移
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
// 调用空构造方法时
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
ArrayList 的扩容机制有点特殊的点在于当没有指定初始容量或者初始容量为0的时候在第一次添加数据的时候就会进入扩容逻辑,是延迟创建对象的:
- 指定初始化初始容量为0时第一次添加元素创建1个长度的底层数组。
- 没有指定初始容量时第一次添加元素创建默认长度为10的底层数组。
Vector和ArrayList的异同:
- 正常情况下底层数组初始大小都为10。
- Vector 可以指定初始的扩容因子,并且在没有指定的情况下默认扩容一倍大小;而 ArrayList 不能指定扩容因子的大小,正常情况下每次扩容原来的1/2。
- Vector 的操作都是同步的,使用 synchronized 关键字进行修饰,而 ArrayList 的操作并不是同步的。
- ArrayList 在初始化容量为0或者没有指定初始化容量的时候做了一些优化,可以实现延迟创建对象的效果。
2.3 LinkedList
底层用双向链表存储数据
- 类的定义和关键字段
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
// 元素数量
transient int size = 0;
// 指向头节点
transient Node<E> first;
// 指向尾节点
transient Node<E> last;
// 空构造
public LinkedList() {
}
// 链表中节点的结构
private static class Node<E> {
E item;
// 指向下一个元素
Node<E> next;
// 指向上一个元素
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
从链表中节点的定义可以看出链表是一个双向链表结构。
- 添加操作
// 头插
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
final Node<E> f = first;
// 创建节点并连接
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
// 尾插
public void addLast(E e) {
linkLast(e);
}
void linkLast(E e) {
final Node<E> l = last;
// 创建节点并连接
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public boolean add(E e) {
// add默认使用尾插
linkLast(e);
return true;
}
- 查询操作
// 查询操作
Node<E> node(int index) {
// 前一半位置从头开始查,
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 后一半位置从尾开始查
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
- 其他
public E peek() {} // 获取第一个元素
public E poll() {} // 删除第一个元素并返回
public E remove() {} // 移除第一个元素
public void push(E e) {} // 插入第一个元素的位置
public E pop() {} // 弹出第一个元素
总结
参考:
JDK1.8源码