Vector和ArrayList的故事

1 首先讲讲Vector

在Java2之前,要存储多个数据,是存储在Vector类中的。

通过读源代码发现Vector类的底层其实是一个Object数组,另外重要的是Vector类中的方法是同步的(synchronized)。

protected Object[] elementData;

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

存储原理:
1.表面上把数据存储到Vector中,其实底层依然是把数据存储到Object数组中的。
2.我们发现该数组的元素类型是Object类型,意味着其中能存储任意类型的对象。
注:集合中只能存储对象,不能存储基本数据类型的值

在Java5之前,必须对基本数据类型手动装箱。
如:v.addElement(Integer.valusOf(123));
从Java5开始,支持自动装箱操作
如:v.addElement(123);
其底层依然是手动装箱。

3.集合类中存储的对象,都存储对象的引用,而不是对象本身。

呃,Vector里的方法就不提了,那是API要讲的东西。

2 下面说说ArrayList

ArrayList类是Java集合框架出现之后用来取代Vector类的(since 1.2)
二者底层原理都是基于数组的算法,一模一样。

3 区别

3.1 容量上的区别

3.1.1 Vector

初始化

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

扩容

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

capacityIncrement参数可以在初始化时指定,未指定时默认是0,如果未指定capacityIncrement,则以2 * oldCapacity扩容。

3.1.2 ArrayList

初始化

在Java7之前,即使使用new ArrayList创建对象,一个元素都不存储,但是在堆空间依然初始化了容量为10的Object数组(size还是0)。

public ArrayList() {
        this(10);
}

而Java7之后的代码:
ArrayList构造一个默认初始容量为10的空列表。

1.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; size = 0;
2.当向数组中添加第一个元素时,通过add(E e)方法中调用的
ensureCapacityInternal(size + 1)方法,即ensureCapacityInternal(1)
3.在ensureCapacityInternal(int minCapacity)方法中,可得的
minCapacity=DEFAULT_CAPACITY=10,然后再调用
ensureExplicitCapacity(minCapacity)方法,即
ensureExplicitCapacity(10)
4.在ensureExplicitCapacity(minCapacity)方法中调用grow(minCapacity)方法,即grow(10),此处为真正具体的数组扩容的算法,在此方法中,通过elementData = Arrays.copyOf(elementData, 10)具体实现了elementData数组初始容量为10的构造。

摘自:http://blog.csdn.net/jdsjlzx/article/details/52675726

简单讲就是:
Java7开始,new ArrayList()之后,底层创建了一个空数组,然后在第一次调用add方法的时候,才重新去初始化数组,容量设置为10.

扩容

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);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
}

newCapacity = oldCapacity + (oldCapacity >> 1);等价于
newCapacity = oldCapacity + (oldCapacity / 2);也就是
1.5 * oldCapacity扩容

3.2 并发上的区别

Vector:所有的方法都使用了synchronized修饰符。线程安全但是性能较低。适用于多线程环境。
ArrayList:所有的方法都没有使用synchronized修饰符。线程不安全但是性能较高。

但,即使以后在多线程环境下,我们也不使用Vector类。
推荐使用下面的:

ArrayList list = Collections.synchronizedList( new ArrayList() );

源代码我就不放了,在同步的实现上和Vector的区别就是:
Vector是使用同步方法实现,synchronizedList使用的是同步代码块实现。

分析:
1.我们都知道加同步锁会一定程度上影响性能,而加锁的范围与性能成反比。
2.同步代码块比同步方法加锁更加灵活(范围可自定义)
3.同步代码块可以选择对哪个对象加锁,而同步方法只能给this对象加锁。

所以说二者比较起来,synchronizedList更加灵活,因为可以指定加锁的对象

但是需要注意一下:
synchronizedList中并没有 使用synchronized代码块。

所以在遍历的时候,要手动进行同步处理,另外其自身具备的方法可以同步,其自身不具备的方法,需要手动同步。例如:

@NotThreadSafe  
class BadListHelper <E> {  
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

    public synchronized boolean putIfAbsent(E x) {  
        boolean absent = !list.contains(x);  
        if (absent)  
            list.add(x);  
        return absent;  
    }  
} 


@ThreadSafe  
class GoodListHelper <E> {  
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

    public boolean putIfAbsent(E x) {  
        synchronized (list) {  //获得list锁对象,其他线程将不能获得list锁来来改变list对象。  
            boolean absent = !list.contains(x);  
            if (absent)  
                list.add(x);  
            return absent;  
        }  
    }  
}  

摘自:http://blog.csdn.net/baidu_37464759/article/details/77683588

上面的代码A线程执行完boolean absent = !list.contains(x);后,B线程获得list对象的锁,如果对list的值进行修改,接着A线程再向后执行,就会造成java.util.ConcurrentModificationException异常。
上面的代码是对this加锁,下面的代码是对list加锁,要注意同步锁对象的选择是否正确。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器