这个问题主要是考察集合框架的问题,主要考察三者之间的设计区别,以及使用时如何选择。然后继续可能考察排序算法的问题。
主要区别:
- Vector是java早期提供的线程安全的动态数组;ArrayList也是动态数组,但不是线程安全的;而LinkedList与前者不同,LinkedList是使用双向链表存储的,也不是线程安全的。
- Vector因为是线程安全的,所以在使用时性能比ArrayList要差。
- Vector和ArrayList可以根据需要自动增加容量,Vector在扩容时是增加1倍,ArrayList在扩容时是增加50%
- LinkedList是使用双向链表,每个数据结点中都有两个指针,分别指向直接前驱和直接后继。所以插入或删除时速度很快,但随机访问时速度比ArrayList慢。
- Vector和ArrayList内部元素是以数组形式存储的,所以适合随机访问。除了在头部和尾部插入或删除元素速度会快一点,其他位置性能会相对较差。比如在中间插入一个元素,需要移动后面所有元素。
通过下面一张图,再看一下三者的关系。
在java集合框架中,Collection接口是所有集合的根,Collection继承Iterable。
然后扩展开提供三大类集合,List、Set、Queue.
- List是有序的集合,也是Vector、ArrayList、LinkedList实现的接口。允许有相同的元素
- Set不允许有重复的元素,这是和List最明显的区别
- Queue是java标准队列的实现,除了集合的基本功能,还支持类似先入先出或者后入先出的特定行为。
再回到Vector、ArrayList、LinkedList,来看下代码实现。
Vector现在很少使用,更多的使用ArrayList。
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable { }
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}
在实现和继承方面Vector和ArrayList一致,LinkedList不同。
通过代码看一下,在动态扩容方面的区别:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容时增加1倍
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容时增加50%
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
由于LinkedList是双向链表,不存在容量限制,所以不需要扩容,代码如下:
public boolean add(E e) {
linkLast(e);
return true;
}
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++;
}
由于Vector、ArrayList是动态数组,所以如何转化为数组呢?List提供了两个方法:
//返回一个包含 List 中所有元素的数组;
Object[] toArray();
//作用同上,不同的是当参数 a的长度比 List 的元素大时,会使用参数a保存List中的元素;否则会创建一个新的 数组存放 List 中的所有元素;
<T> T[] toArray(T[] a);
ArrayList实现如下:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
LinkedList转为数组的实现如下:
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
//一个一个赋值,没有ArrayList使用copy快
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}