ArrayList
1、ArrayList介绍
ArrayList是一种线性数据结构,它的底层是用数组实现的,相当于动态数组。与Java中的数组相比,它的容量能动态增长,默认容量大小为10
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
/**
* 构造初始容量为10的空列表。本质上为一个数组结构的集合
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
当创建一个数组的时候,就必须确定它的大小,系统会在内存中开辟一块连续的空间,用来保存数组,因此数组容量固定且无法动态改变。ArrayList在保留数组可以快速查找的优势的基础上,弥补了数组在创建后,要往数组添加元素的弊端。实现的基本方法如下:
1. 快速查找:在物理内存上采用顺序存储结构,因此可根据索引快速的查找元素。
2. 容量动态增长: 当数组容量不够用时,创建一个比原数组容量大的新数组(原来的一半),将数组中的元素复制到新数组,再将新数组赋给原数组成员变量,最后将新元素放进数组中。源码如下:
//将数据增加到集合中
public boolean add(E e) {
//确定ArrayList的容量大小
ensureCapacityInternal(size + 1); // Increments modCount!!
//添加e到ArrayList中
elementData[size++] = e;
return true;
}
//返回容器元素数量,加入新元素后的容量跟初始化容量对比
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 判断容器是否溢出,即超出初始化大小,超出就自动增加容量
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 增加容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//若当前容量不足以容纳当前的元素个数,设置 新的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 创建新的数组(大小为newCapacity),将原数组中的元素复制到新数组,并将新数组赋值给elementData 成员变量,
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结,向集合中添加元素实现动态规则如下步骤:
(1)判断集合元素的个数与集合初始化大小对比
(2)增加元素后的个数大于默认容器大小,则创建新数组(数组大小为原来的一半),并将原数组元素复制到新数组中
(3)将新创建的数组赋予集合中数组成员变量elementData
(4)数组成员变量接受新元素
2、ArrayList遍历方式
(1) 第一种,通过迭代器遍历。即通过Iterator去遍历。
Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
(2) 第二种,通过索引值去遍历。
Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
value = (Integer)list.get(i);
}
(3) 第三种,for循环遍历。如下:
Integer value = null;
for (Integer integ:list) {
value = integ;
}
比较这3种方式的效率:,遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!
3、ArrayList线程不安全
略
4、ArrayList性能分析
(1)查询速度快
ArrayList底层本身以数组的结构存储数据,初始化时默认创建容量为10的数组,并具有一个的排列顺序。通过索引能快速定位到数组的具体位置,并获取到该位置上的元素;
(2)增加或删除元素时,操作的位置不同,对数组存储数据的性能影响也不用
当在数组的尾部新增(或删除)元素,则在该位置上直接添加新元素(或删除、清空该元素),其他的位置上的数组元素保持不变,操作的速度非常的快;
但是,当新增(或删除)的元素位置不在首尾两端,而是在其中的某一个位置,则这过程将涉及到数组元素的内存问题,新增或删除指定位置的元素时,后面的元素将向前移动位置,填充该位置,达到数组的连续性,在元素移动位置过程中,将大大的降低了数组存储数据的效率;
Vector
1、Vector本质上为动态数组结构,默认容量为10,其的构造函数如下:
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
public Vector() {
this(10);
}
public Vector(int initialCapacity) {
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;
}
2、添加元素时,线程安全
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
//判断容器大小,是否溢出,过程类似于ArrayLIst
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
3、Vector与ArrayList的区别
(1)同步性:
Vector集合线程安全,效率低;ArrayList线程不安全,效率高
(2)数据增长规则(容器扩张规则)
Vector与ArrayList的默认容器大小均为10,可自定义容器大小,不同之处在于:当增加的元素超出容量本身的大小时,集合容器大小自动动态的增长,Vector容器增加为原来的一倍,ArrayList容器增加为原来的一半
4、ArrayList,Vector, LinkedList的存储性能和特性
ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据便自动扩容,以增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
LinkedList也是线程不安全的,LinkedList提供了一些方法,使得LinkedList可以被当作堆栈和队列来使用