您的位置:首页 > 编程语言 > Java开发

浅析Java集合类源码(一)--- Vector, ArrayList, LinkedList

2016-09-15 12:26 495 查看

1. Vector

Vector类继承 AbstractList ,实现了 List,RandomAccess,Cloneable,Serializable等接口,并且Vector大多数方法都有synchronized关键字,说明Vector是线程安全类。

public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable


(1)初始化

Vector 类初始化时需设置初始容量(initialCapacity)和容量增长率(capacityIncrement),默认初始容量为10,容量增长率为0

public Vector(int initialCapacity, int capacityIncrement)
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}


(2)查找

Vector采用数组来对对象进行存储

protected Object[] elementData;


因此,可通过下标来查找对象,例如 : get(),set(),firstElement(),lastElement(),setElementAt(),insertElementAt()等方法的时间复杂度是O(1)

public synchronized E get(int index)
public synchronized E set(int index, E element)
public synchronized E firstElement()
public synchronized E lastElement()
public synchronized void setElementAt(E obj, int index)
public synchronized voidinsertElementAt(E obj, int index)


(3)添加

当调用add()添加元素时,需要调用ensureCapacityHelper ()确保当前数组元素数量小于数组长度,否则调用grow()增长数组

ensureCapacityHelper(elementCount + 1);
if (minCapacity - elementData.length > 0)
grow(minCapacity);


grow()方法,当设置了capacityIncrement时,原数组增长为(oldCapacity + capacityIncrement),否则增长为原数组的2倍((oldCapacity + oldCapacity))

int newCapacity = oldCapacity +
((capacityIncrement > 0) ? capacityIncrement : oldCapacity);


然后将原数组内容复制到新容量的数组中,新数组增长容量的值设置为null

elementData = Arrays.copyOf(elementData, newCapacity);


因此,添加数组元素时,时间复杂度可能是O(1),也可能是O(n)

(4)删除

当需要移除某个对象时,使用removeElementAt(intindex) 方法。此时,removeElementAt方法会将数组的后半部分((index+1) ~ elementCount)的所有元素复制到index ~ (elementCount-1) 的位置,再将elementCount 置为null,以帮助GC(垃圾回收器)回收内存。平均时间复杂度是O(n)

int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */


当使用remove(Object) 方法删除元素时,需要调用indexOf(Object) 方法扫描整个数组,时间复杂度是O(n)

2. ArrayList

ArrayList类继承 AbstractList ,实现了 List,RandomAccess,Cloneable,Serializable等接口,ArrayList是非线程安全类。

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable,
java.io.Serializable


(1)初始化

ArrayList初始化时需要初始容量,默认为10,但并不是直接生成一个长度为10的数组,而是赋值一个空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,再在ensureCapacity中判断元素数组是EMPTY_ELEMENTDATA还是DEFAULTCAPACITY_EMPTY_ELEMENTDATA。若是

DEFAULTCAPACITY_EMPTY_ELEMENTDATA 再生成长度为10的数组。

private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] 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() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
}


(2)查找

通过get(int index) 方法可查找特定下标的元素,此方法直接从elementData[] 数组中取元素,时间复杂度是O(1)

public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}


(3)添加

通过add() 方法可往数组中添加元素,需要调用ensureCapacityInternal保证数组的长度,再调用ensureExplicitCapacity,当需要时调用grow方法来扩展数组长度。

ensureCapacityInternal(size + 1);

ensureExplicitCapacity(minCapacity);

if (minCapacity - elementData.length > 0)
grow(minCapacity);


grow() 方法,扩展原有数组,扩展后的数组长度为原来数组的1.5倍。但是当数组需要的最小容量大于原数组的1.5倍是,则扩展为需要的最小容量。增加的数组元素是null。

int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);


在添加时,最好时间复杂度是O(1),最坏时间复杂度是O(n)(需要复制数组),平均时间复杂度是O(n)

(4)删除

通过remove(index) 方法删除元素时,此时,remove方法会将数组的后半部分((index+1) ~ size)的所有元素复制到index ~ (elementCount-1) 的位置,再将size置为null,以帮助GC(垃圾回收器)回收内存,平均时间复杂度O(n)。

int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work


通过remove(Object o)对某个对象进行移除时,需要对整个数组进行扫描,获取此对象第一次出现的位置index,再调用fastRemove来移除此对象。fastRemove方法和remove(int index)方法类似,也是将数组的后半部分((index+1) ~ size)的所有元素复制到index ~ (elementCount-1) 的位置,再将size置为null,以帮助GC(垃圾回收器)回收内存。扫描数组和复制数组的平均时间复杂度均是O(n)。

for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}

private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work
}


3. LinkedList

LinkedList类继承 AbstractSequentialList ,实现了 List,Deque,Cloneable,Serializable等接口, LinkedList是非线程安全类。LinkedList的基本数据结构是双向链表。

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable


(1)初始化

LinkedList初始化并没有构造实质的列表。

public LinkedList() {
}


(2)查找

通过get(int index) 方法查找某个下标的元素,get() 方法调用node(int index)方法查找元素。

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}


node(int index)方法先判断index位于链表的前半部分还是后半部分。若是位于前半部分,则从第一个元素开始向后遍历;若是位于前半部分,则从最后一个元素开始向前遍历。平均时间复杂度是 O(n/4)。

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;
}
}


其他查找方法,如:getFirst(),getLast()的时间复杂度是O(1)

(3)添加

使用add() 方法添加元素时,会使用linkLast() 将元素添加到数组的末尾,时间复杂度是O(1)

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;


(4)删除

当通过remove(int index) 方法移除指定下标元素时,其调用unlink方法断开连接

public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}


因为LinkedList是双向链表结构,unlink方法首先会断开删除元素element与前一个元素的相互连接,再断开删除元素element与后一个元素的相互连接。

final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;

if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}

if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}

x.item = null;


由于调用remove方法时需要查找index的位置,因此平均时间复杂度是O(n/2)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息