Java 单列集合之List
类的继承关系
实现子类 |
---|
ArrayList |
LinkedList |
Vector |
Iterable 接口
Collection 接口
List 接口
ArrayList
属性字段
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;
// List中的元素数量
private int size;
底层存储
ArrayList使用Object对象
存储数据,当ArrayList没有明确指定泛型的类型时,ArrayList可以存放各种类型的对象。
初始化
// 带有初始容量的构造函数
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);
}
}
// 无参构造,默认容量是10,是懒加载的
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
扩容机制
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算当前需要的容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 判断是否是默认大小的List,惰性初始化
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 需要的容量比当前的容量大则需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容
private void grow(int minCapacity) {
// 保存原先的容量
int oldCapacity = elementData.length;
// 计算新的容量 = 1.5 * oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 判断新的容量是否能够满足minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 不满足则直接更新为minCapacity
// 判断新的容量是否超过最大容量的限制
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 通过Arrays.copyOf生成新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 获取最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// MAX_ARRAY_SIZE 为 Integer.MAX_VALUE - 8
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
总结
- 是线程不安全的
- 底层是通过
Object数组
存储的 - 默认容量是
10
,并且是惰性加载
的 - 扩容机制是每次扩容
1.5
倍 - 最大容量是size属性可表示的容量
Integer,MAX_VALUE = 2^31 -1
- 查询操作是O(1)的,支持随机查询
- 增删操作是O(N)的,需要移动数据
LinkedList
属性字段
// 大小
transient int size = 0;
// 首结点
transient Node<E> first;
// 尾结点
transient Node<E> last;
底层存储
是双向链表结构存储的
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;
}
}
容量是否有限制
LinkedList到底有没有容量限制值得商讨,在LinkedList的添加操作中并没有容量的检测和限制,理论上来说LinkedList是没有容量限制的,可以一直往里面添加新的结点。
但是由于属性size有一定的范围,最大值为2 ^ 31 -1
,如果超过这个大小,list.size()得到的结果将会是不正确的。
以下代码通过两个字节的short类型来模拟这个过程
package com.collection.list;
class LinkedList{
int size = 0;
void add(){
// new Node
size++;
}
}
public class LinkedListTest1 {
public static void main(String[] args) {
LinkedList list = new LinkedList();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
list.add();
}
System.out.println("bound_size:" + list.size);
list.add();
System.out.println("error_size:" + list.size);
}
}
/*
bound_size:2147483647
error_size:-2147483648
*/
总结
- 是线程不安全的
- 是双向链表存储的
- 没有容量限制,但由于size可表示的范围最大为
2 ^ 31 - 1
,超过这个范围会导致获取不到正确的大小 - 是有序的
- 查询操作O(N),无法随机查询
- 增删操作O(1)
Vector
属性字段
// 对象数组存储元素
protected Object[] elementData;
// 元素个数
protected int elementCount;
// 容量增量
protected int capacityIncrement;
private static final long serialVersionUID = -2767605614048989439L;
初始化
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
// 创建容量,没有惰性加载
this.elementData = new Object[initialCapacity];
// 设置容量增量
this.capacityIncrement = capacityIncrement;
}
// 带有初始化容量的构造函数,增量0
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
// 默认初始容量10,增量0
public Vector() {
this(10);
}
扩容机制
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 保存原数组的大小
int oldCapacity = elementData.length;
// 计算新的容量
// 1. 如果增量大于0,则扩充为 oldCapacity + capacityIncrement
// 2. 如果增量不大于0,则扩充为 2 * oldCapacity
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
// 检查新的容量是否够用
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 检查新的容量是否超过最大限制
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 扩充到新的容量
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结
- 是线程安全的
- 默认容量是10,默认增量是0,没有惰性加载
- 如果增量大于0则线性扩增
oldCapacity + capacityIncrease
,如果增量不大于0则指数扩增2 * oldCapacity
- 最大容量是
Integer.MAX_VALUE
- 是有序的
- 查询操作O(1),支持随机查询
- 增删操作O(N),需要移动数组
三个实现子类对比
特点 | ArrayList | Vector | LinkedList |
---|---|---|---|
线程安全 | 不安全 | 安全 | 不安全 |
底层存储 | Object数组 | Object数组 | 双向链表 |
默认容量 | 10 | 10 | - |
最大容量 | Integer.MAX | Integer.MAX | - |
惰性加载 | 默认容量惰性加载 | - | - |
扩容机制 | 1.5倍 | 增量 或 2倍 | - |
是否有序 | 是 | 是 | 是 |
查询操作 | O(N) | O(N) | O(1) |
增删操作 | O(1) | O(1) | O(N) |