Vector

Vector集合是 jdk1.0 版本就提供的集合类,它的所有操作集合的方法,都进行了加锁(就是对方法使用synchronized修饰)。这样就可以防止多线程同时修改集合。

因为是方法级synchronized锁,那么Vector集合的效率就不高。而且使用了Vector集合并不一定在多线程情况下就没有问题。

Vector不一定就是线程安全

很多人觉得不可思议,我们已经使用方法级synchronized锁,那么同一时刻只能由一个线程来操作集合啊,怎么还会有线程安全问题。

使用synchronized锁,我们可以保证对集合操作的原子性,这个是毋庸置疑的。但是我们对集合遍历却是两个操作。想一想我们怎么使用迭代器Iterator进行集合遍历的,我们是先调用hasNext方法,再调用next方法获取值。我们即使给两个方法都加了锁,保证操作原子性。但是它们合起来却不是原子性的。

那么就有可能出现下面情况,我们使用hasNext方法返回true,说明集合中还有值,正准备调用next方法时,另一个线程突然将这个值移除了(因为hasNext方法和next方法合起来没有加锁),这个就是问题产生原因。

因为hasNext方法和next方法是由用户自己调用的,我们没办法要求用户进行加锁,而这种情况可能会发生,怎么办呢?所以就用ConcurrentModificationException这个异常来代表这个情况。所以现在可以明白AbstractList中成员属性modCount的重要作用了吧。

注意:Vector提供的枚举器Enumeration却没有这个功能,它不能判断多线程环境下进行遍历时,是否有其他线程修改这个集合。只有当发生最严重的情况,即集合中只剩下最后一个元素,却被删除了,这时调用nextElement方法,抛出NoSuchElementException异常。

具体实例代码在文中末尾,所以Vector这个集合尽量少用,因为它既不能保证多线程一定安全,而且效率还低。

成员属性

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

可以看出Vector和ArrayList所继承和实现的接口一模一样。

// 用elementData数组来存储集合中的元素
protected Object[] elementData;

// 集合中元素数量
protected int elementCount;

// 每次扩容时,多增加的大小。如果是0的话,那么每次扩容的时候,都是原数组长度的一倍
protected int capacityIncrement;

对比ArrayList,Vector的成员属性更加简洁一点。

  • elementData Object数组用来存储集合中元素。
  • elementCount表示集合中元素的数量。
  • capacityIncrement 表示数组每次扩容固定增加的数量。
  • 还有一个重要属性就是继承自AbstractList的modCount属性。

构造函数

1、默认的构造函数

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

public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

Vector构造函数就比ArrayList简单明了多了,空参构造函数和一个参数构造函数都是通过默认值,来调用两个参数构造函数。主要是用来创建对应长度的数组。

注意:这里没有进行数组的延迟创建,所以每创建一个Vector集合,都会创建一个数组。

2、通过Collection实例构建集合

public Vector(Collection<? extends E> c) {
    elementData = c.toArray();
    elementCount = elementData.length;
    // c.toArray()方法返回的可能并不是Object[]数组
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

通过toArray方法,将集合c转成数组,然后赋值给elementData变量。还要注意一点就是,c.toArray()方法返回的可能并不是Object[]数组,所以这里做了判断。

重要方法

数组扩容

// 这个方法是给集合外部调用的,确保数组长度不能小于集合元素数量
public synchronized void ensureCapacity(int minCapacity) {
    if (minCapacity > 0) {
        modCount++;
        ensureCapacityHelper(minCapacity);
    }
}

这个方法给集合外部调用的,来确保数组长度不能小于集合元素数量。因为是外部调用,所以使用synchronized进行加锁。

// Vector集合内部调用,新添数据的时候会调用。   确保数组长度不能小于集合元素数量
private void ensureCapacityHelper(int minCapacity) {
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

集合内部调用,新添数据的时候会调用。这里没有使用synchronized加锁,是因为调用的地方,都已经加过锁了。

private void grow(int minCapacity) {
    // 获取老数组长度
    int oldCapacity = elementData.length;
    // 如果capacityIncrement大于0,那么就增加这个固定长度。
    // 否则就是将老数组长度扩大一倍
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    // 如果进行扩容之后,还是小于minCapacity值,那么就直接用minCapacity值当成数组长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 处理newCapacity值大于MAX_ARRAY_SIZE情况,集合容量也是有上限的。
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 使用Arrays.copyOf方法,将老数组的元素拷贝到newCapacity长度的新数组中,并返回这个新数组。
    elementData = Arrays.copyOf(elementData, newCapacity);
}

进行数组扩容,如果capacityIncrement大于0,那么就在原长度基础上加上这个固定数值capacityIncrement,否则就将原长度扩大一倍。如果还是小于minCapacity,那么就直接使用minCapacity作为新数组长度,然后再处理超出最大数组长度问题。最后使用Arrays.copyOf方法,将老数组元素拷贝到新数组中。

添加元素

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

在集合末尾添加元素,先确保数组容量,然后再在集合末尾添加元素,最后将集合数量size加一。

public void add(int index, E element) {
    insertElementAt(element, index);
}

public synchronized void insertElementAt(E obj, int index) {
    modCount++;
    if (index > elementCount) {
        throw new ArrayIndexOutOfBoundsException(index
                                                 + " > " + elementCount);
    }
    ensureCapacityHelper(elementCount + 1);
    System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
    elementData[index] = obj;
    elementCount++;
}
add(int index, E element)方法是调用insertElementAt方法,而且在add方法中没有加锁,因为在add方法中没有操作集合。
在数组index下标位置插入一个元素,先确保数组长度,再调用System.arraycopy方法,将index位置之后的元素右移一位。然后在index位置存入新添加数据,最后将集合数量elementCount自增。
public synchronized boolean addAll(Collection<? extends E> c) {
    modCount++;
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityHelper(elementCount + numNew);
    System.arraycopy(a, 0, elementData, elementCount, numNew);
    elementCount += numNew;
    return numNew != 0;
}

在集合末尾添加一个集合c中全部元素,将集合c转化成一个数组a,确保本集合数组长度够用,通过System.arraycopy方法将数组a中所有元素全部拷贝到本集合末尾,最后将集合数量增加。

public synchronized boolean addAll(int index, Collection<? extends E> c) {
    modCount++;
    if (index < 0 || index > elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityHelper(elementCount + numNew);

    int numMoved = elementCount - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    elementCount += numNew;
    return numNew != 0;
}

与addAll(Collection<? extends E> c)相比,它多了将index位置之后的元素全部右移新添加集合数量numNew的长度。正好可以空出index 到index + numNew位置来存放添加的集合。

删除元素

public boolean remove(Object o) {
    return removeElement(o);
}

public synchronized boolean removeElement(Object obj) {
    modCount++;
    int i = indexOf(obj);
    if (i >= 0) {
        removeElementAt(i);
        return true;
    }
    return false;
}

public synchronized void removeElementAt(int index) {
    modCount++;
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }
    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 */
}

删除某个元素最终都会调用删除某个位置index的removeElementAt方法。而删除数组某个位置元素,其实只要将index之后位置元素向前移动一位就可以了,注意一点 要将数组中无效位置的数据置位null(而这里的无效位置就是elementCount)。

public synchronized E remove(int index) {
    modCount++;
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    E oldValue = elementData(index);

    int numMoved = elementCount - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--elementCount] = null; // Let gc do its work

    return oldValue;
}

与removeElementAt方法流程一样,只不过这个方法返回了被删除元素。

public synchronized boolean removeAll(Collection<?> c) {
    return super.removeAll(c);
}

public synchronized boolean retainAll(Collection<?> c) {
    return super.retainAll(c);
}

这里复写方法,就是为了给该方法加上synchronized锁,这个不改变实现逻辑,进行同步的最简单方式,你会发现Collections工具类提供的synchronizedXXX方法都是这个做的。

和ArrayList相比,Vector没有做什么优化,而是调用AbstractCollection对应方法,是通过迭代器的remove方法进行删除的。
public void clear() {
    removeAllElements();
}
public synchronized void removeAllElements() {
    modCount++;
    // Let gc do its work
    for (int i = 0; i < elementCount; i++)
        elementData[i] = null;

    elementCount = 0;
}

加了synchronized锁,将数组中所有元素置位null,方便垃圾回收器回收。

public synchronized void setSize(int newSize) {
    modCount++;
    if (newSize > elementCount) {
        ensureCapacityHelper(newSize);
    } else {
        for (int i = newSize ; i < elementCount ; i++) {
            elementData[i] = null;
        }
    }
    elementCount = newSize;
}

这个是Vector独有的比较有意思的方法,它也可以删除集合中的元素。如果新设的数量newSize比原数量elementCount小,那么数组newSize之后的元素全部置位null。

替换元素

public synchronized E set(int index, E element) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

就是将数组对应位置替换成新元素element。

查询元素

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

返回对应索引位置元素,并强转成E类型。注意这个方法也没有使用synchronized。所以它肯定在别的synchronized方法中调用的,第二这个方法的修饰符是default,即外部是没有办法调用它的。

public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    return elementData(index);
}

通过elementData方法获取元素。

public int indexOf(Object o) {
    return indexOf(o, 0);
}

public synchronized int indexOf(Object o, int index) {
    if (o == null) {
        for (int i = index ; i < elementCount ; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = index ; i < elementCount ; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

正向遍历数组,查找与o相同元素,如果查找到就返回对应下标位置,如果没有查到就返回-1。

public synchronized int lastIndexOf(Object o) {
    return lastIndexOf(o, elementCount-1);
}

public synchronized int lastIndexOf(Object o, int index) {
    if (index >= elementCount)
        throw new IndexOutOfBoundsException(index + " >= "+ elementCount);

    if (o == null) {
        for (int i = index; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = index; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

反向遍历数组,查找与o相同元素,如果查找到就返回对应下标位置,如果没有查到就返回-1。

public boolean contains(Object o) {
    return indexOf(o, 0) >= 0;
}

contains方法是通过indexOf方法实现的,它也没有使用synchronized,因为在indexOf方法中会使用同步。

public synchronized Iterator<E> iterator() {
    return new Itr();
}

public synchronized ListIterator<E> listIterator() {
    return new ListItr(0);
}

public synchronized ListIterator<E> listIterator(int index) {
    if (index < 0 || index > elementCount)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}

与AbstractList一样,Vector提供两个迭代器,都是Vector内部类。Vector的内部类Itr 与ArrayList的内部类Itr的实现几乎一样。只不过是加了synchronized锁。

 

posted @ 2022-02-07 11:28  残城碎梦  阅读(165)  评论(0编辑  收藏  举报