1.先看ArrayList:
实现基于Object[],因此ArrayList具有数组特性,每个元素都有索引,查询效率高。相对于数组,ArrayList具有可扩展特性,有自增长机制。但ArrayList不是多线程安全的。
2.Vector:
实现也基于Object[],也有自增长机制。但Vector是多线程安全的,所以性能低。
3.性能解析
- Vector中很多方法都是有synchronized修饰,多线程安全,但也导致效率低于ArrayList。
- 实现都基于Object[],但空间不足时,两个类的增长方式不一样。
- 容量增量 默认Vector增加原来空间的一倍 。
- 容量增量 默认ArrayList增加原来空间 的0.5倍。
4.源码分析
ArrayList构造器有三种:
/** 无参构造初始容量为10
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/** 传一个初始化容量值的构造
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
/** 泛型值集合传参的构造
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
/** @return an array containing all of the elements in this collection
Object[] toArray();*/
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
上面代码中:
Object[] elementData = c.toArray();
但是 elementData.getClass() 不一定等于 Object[].class 需要用 Arrays.copyOf(elementData , size , Object[].class);转换。这是为何呢?
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)
可以想到大概是 c.toArray()的实现方式不一样,导致 getClass()不一定为Object[] ,所以看看 c.toArray() 是如何解释的?(如下)
@return an array containing all of the elements in this collection
我们用代码测试一下:
//集合类测试
public class CollectionTest {
public static void main(String [] args){
List<String> list = Arrays.asList("abc"); /*
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a); //不是用到公共的ArrayList,而是用到Arrays的静态内部类ArrayList
}
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
@Override
public Object[] toArray() {
return a.clone(); //返回的是a类型的数组
}
}
*/
// class java.util.Arrays$ArrayList
System.out.println(list.getClass());
// class [Ljava.lang.String; 所以返回的是 String数组
Object[] objArray = list.toArray();
System.out.println(objArray.getClass());
objArray[0] = new Object(); // 报错,是String数组向上转型成Object,元素如果为object不一定是string,所以报错。但string可以是object
//
List<String> dataList = new ArrayList<String>(); //这个里面就是上面的已经成为Object[]了
dataList.add("one");
dataList.add("two");
Object[] listToArray = dataList.toArray();
// class [Ljava.lang.Object; 所以返回的是Object数组 ,里面可以存任何东西
System.out.println(listToArray.getClass());
listToArray[0] = "";
listToArray[0] = 123;
listToArray[0] = new Object();
}
}
所以可以看到 toArray()方法还是有区别的,因为重写过了。
再看看ArrayList的容量增量是如何实现的,代码如下:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 新容量加1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//新的容量如果将要大于已经有的容量,就增长容量grow()
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //增加原来的0.5倍,共1.5
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); //容量扩展
}
下面介绍Vector的构造器:4种
/**
* Constructs an empty vector with the specified initial capacity and
* capacity increment.
*
* @param 初始量initialCapacity the initial capacity of the vector
* @param 增量capacityIncrement the amount by which the capacity is
* increased when the vector overflows
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
上面代码中 capacityIncreament=0 在自增方法中,新容量为倍增,新容量=旧容量+旧容量。如果不等于0,新容量=旧容量+增量
再看看它的自增是如何实现的:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1); //容量先加1
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0) //如果新容量将大于旧容量
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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;
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
- 为什么数组最大容量=Integer.MAX_VALUE - 8 ?
意思是有些虚拟机在数组中保留了一些头信息。避免内存溢出!
是否减8没那么重要(只是为了避免一些机器内存溢出),最大长度依然是Integer.MAX_VALUE,并不是Integer.MAX_VALUE-8,,如果碰到
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
所说的情况,程序员又不知道这种情况依然添加内容,那oom也是在所难免的。。
所以说这个 -8 是为了减少出错的几率。