JAVA源码分析 Vector 和 ArrayList

lingyuli
发布于 2020-9-8 10:20
浏览
0收藏

源码分析 Vector 和 ArrayList 

每次我们会被问到这个问题,我们都知道 Vector 是线程安全的,而 ArrayList是线程不安全的。今天想引申出几个问题。

 

Vector 和 ArrayList 还有什么不同
Vector 容器是如何实现线程安全的
如果在多线程环境下,有没有别的 List 容器推荐,分别使用在什么场景?

 

首先回答第一个问题 Vector和ArrayList还有什么不同?

JAVA源码分析 Vector 和 ArrayList-鸿蒙开发者社区

上图我们可以看到,Vetor和ArrayList都继承自AbstractList,所以大体上对外提供的接口大致相同,只是内部实现不同,底层都是基于数组加上扩容机制实现,除线程安全外Vetor和ArrayList最大的不同就是扩容方式不同,我们先给出结论,Vector在扩容时是提高一倍容量,而ArrayList是50%。下面我们可以具体分析一下扩容的源码。

Vector
初始化方法

/*
  @param   initialCapacity     内部数组的初始容量,默认为0
  @param   capacityIncrement   每次扩容的容量,如果不设置则成倍扩容
 /
public Vector(int initialCapacity, int capacityIncrement);

/*
  @param   initialCapacity     初始容量
 /
public Vector(int initialCapacity);

/
  初始化一个 初始容量为10,每次扩容一倍的Vertor容器
 */
public Vertor();

我们从add(Object o)入手

public synchronized boolean add(E e) {
    modCount++;
    add(e, elementData, elementCount);
    return true;
}

可以看到add方法是加了synchronized关键字,这就说明Vector的add()方法在同一时刻只有一个线程能获取到锁,一目了然,Vector是使用 synchronized 同步机制实现到线程安全,它的每一个对外提供的 api 上都加有 synchronized 关键字。

 

下面我们看一下 add(e, elementData, elementCount); 这个方法

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    elementCount = s + 1;
}

其中 e代表添加的元素,elementData代表内部的数组,s当前的容量。注意elementData数组的长度和s不是相同的,比如 Vector初始化的时候, elementData的初始化长度是10,而s=0。上面我们可以看到,如果当前容量s = 内部数组的长度以后,就需要调用 grow() 方法获取一个新的 内部数组,扩容的逻辑就在 grow() 方法里面。

 

下面我们看一下 grow() 方法:

private Object[] grow() {
    //elementCount代表内部数组的长度,比如初始化情况下是10。
    return grow(elementCount + 1);
}

//minCapacity 代表最少生成一个容量为minCapacity的数组
private Object[] grow(int minCapacity) {
    //newCapacity(minCapacity) 方法会生成一个新的数组
    return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
}

来了来了,核心扩容逻辑来了 newCapacoty(int minCapacity)

 

private int newCapacity(int minCapacity) {
    //获取当前数组的容量
    int oldCapacity = elementData.length;
    //获取扩容以后新数组的容量
    //注意 capacityIncrement 是初始化 Vector 的时候可以自己设置的,表明每次扩容的大小
    //如果没有设置capacityIncrement,我们可以看到 newCapacity = oldCapacity + oldCapacity
    //也就是默认两倍扩容
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
    //这里是一个极端的情况,我们都知道int类型都是有范围的,如果超过 2^31-1,就会变成负数,这个时候就说明,newCapacity 已经扩容到最大了,超纲了
    if (newCapacity - minCapacity <= 0) {
        //数组已满
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    //这个地方就是保证数据扩容不超过int范围,如果超过了就不扩容了
    //内部数据的容量如果超过了就会OOM
    return (newCapacity - MAXARRAYSIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}


private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAXARRAYSIZE) ?
        Integer.MAXVALUE :
        MAXARRAY_SIZE;
}

 

ArrayList
构造方法

 

/*
  @param initialCapacity 初始容量,默认为10
 */
public ArrayList(int initialCapacity)

public ArrayList();

public ArrayList(Collection<? extends E> c);

我们也从 add(Object o) 入手

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

private Object[] grow() {
    return grow(size + 1);
}

private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
}

 

我们可以看到,上面的方法,除了 add(E e) 方法没有 synchronized 关键字,基本都相同。

 

下面我们看一下 newCapacity(int minCapacity) 这个方法

    private int newCapacity(int minCapacity) {
        //当前数组的容量
        int oldCapacity = elementData.length;
        //新数组的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            //newCapacity 超过2^31-1如何处理
            if (elementData == DEFAULTCAPACITYEMPTYELEMENTDATA)
                return Math.max(DEFAULTCAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAXARRAYSIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAXARRAYSIZE)
            ? Integer.MAXVALUE
            : MAXARRAYSIZE;
    }

 

ArrayList 线程安全处理
3.1 Collections.synchronizedList(new ArrayList); 

3.2 CopyOnWriteArrayList() 容器,基于 ReadWriteLock 锁实现

 

 

来源:InfoQ 

分类
已于2020-9-8 10:28:09修改
收藏
回复
举报
回复
    相关推荐