Java源码阅读Vector

1类签名注释

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

Vector类实现了一个可伸缩的对象数组。和数组一样,他里面的组件能被integer型的索引访问。不同的是,Vector被创建后,当增加或删除项的时候,其size可以增长或收缩来适应大小。

每个Vector都试图通过保持capacity和capacityIncrement来优化存储管理。 capacity总是至少和vector的size一样大,它通常是更大的因为组件添加到Vector,Vector的存储块的大小增加“capacityincrement”。应用程序可以在插入大量组件之前增加Vector的capacity,这减少了增量再分配的数量。

通过该类的iterator()和listIterator(int)方法返回的iterator是fail-fast(快速失败机制)的:

当iterator被创建后,如果vector的结构在任何时候被修改,除了使用iterator自己的ListIterator#remove()或者ListIterator#add(Object)外的任何方法,将会抛出ConcurrentModificationException异常。因此,面对并发修改时,迭代器会快速而干净地失败,而不是在未来的某个不确定的时间进行有风险的行为。被elements()方法返回的Enumeration不是fail-fast的。

值得注意的是fail-fast行为可能是不能得到保证的。因此,编写一个依赖于ConcurrentModificationException异常的程序是错误的:迭代器的fail-fast行为应该只用于检测错误。

在Java 2版本中,这个类被修改为实现List接口,使得它成为java集合框架成员。Vector是synchronized的(线程安全的),如果不需要线程安全的实现,推荐使用ArrayList。(效率更高)

 

2关于容量(capacity)和size的问题

capacity:是指这个Vector能放多少组件,是指数组分配的大小(elementData.length)

size:是指这个Vector里面放了多少组件,用elementCount表示。

下面两个方法分别获取Vector的capacity和size:

 public synchronized int capacity() {
        return elementData.length;
    }

    public synchronized int size() {
        return elementCount;
    }

 

3构造函数

  //capacityIncrement表示每次扩容的增量,若增量大于0,则每次增加增量大小,否则每次扩容为原来的2倍
  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);
    }

   // 默认的容量为10,增量为0
    public Vector() {
        this(10);
    }

   //将Collection集合传进来,构造vector
    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);
    }

 

4容量的可伸缩性

4.1扩容操作

public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            ensureCapacityHelper(minCapacity);
        }
    }
   
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
   
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

通过调用ensureCapacity(minCapacity)进入扩容操作,这里有个minCapacity参数,表示所需的最小容量,具体怎么用请看接下来的代码。

(1)ensureCapacity方法首先判断minCapacity大于0,若是的话进行下一步modCount++,modCount是继承自父类AbstractList的变量,记录集合“结构修改”操作的次数,这种操作主要包括size的改变等。然后调用ensureCapacityHelper(minCapacity)方法。

(2)ensureCapacityHelper方法先判断minCapacity是否大于当前Vector的容量,若小于,则不需要扩容,否则调用真正的扩容方法grow(minCapacity)。

(3)grow方法先拟定一个新容量newCapacity,若增量大于0则newCapacity等于旧容量加增量,否则等于2倍旧容量。然后将newCapacity和minCapacity的较大值赋给newCapacity(newCapacity = Max(newCapacity,minCapacity))。接下来判断若newCapacity小于MAX_ARRAY_SIZE,就将旧数组复制到容量为newCapacity的新数组中。反之,调用hugeCapacity(minCapacity)求newCapacity。

(4)hugeCapacity方法首先判断minCapacity是否小于0,若是,则内存溢出,抛出OutOfMemoryError错误。若否,则继续判断minCapacity是否大于MAX_ARRAY_SIZE,若是则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE。(这里说明,其实vector的容量是可以达到整数最大值的,而不仅仅是整数最大值-8)

这里不太理解第4步为什么minCapacity小于0,就报内存溢出错误。既然容量可以达到整数最大值,那么8>=newCapacity - MAX_ARRAY_SIZE > 0这样不也没超过吗?为什么不用判断(小于等于8)就直接报错。

4.2容量收缩

Vector集合不仅可以扩容,还可以减容,就是把多余的空间给减掉。

 public synchronized void trimToSize() {
        modCount++;
        int oldCapacity = elementData.length;
        if (elementCount < oldCapacity) {
            elementData = Arrays.copyOf(elementData, elementCount);
        }
    }

oldCapacity获取的是数组的大小,也就是前面第二节说的Vector的容量。elementCount表示Vector里面实际放了多少个元素。当elementCount < oldCapacity的时候就有部分空间没有存放数据,这里通过将旧数组复制到新数组上来完成的。(数组的长度不可变)。

4.3 setSize方法

setSize(int newSize)方法功能如下newSize大于当前容量,将Vector扩容到newSize。否则,多余的元素变为null,但是Vector的容量不变(数组实现,有序)。

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

 

5查找

5.1查找元素

查找一个元素是否在Vector集合里面使用contains(Object o)方法。

 public boolean contains(Object o) {
        return indexOf(o, 0) >= 0;
    }
   
    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;
    }

contains方法内部调用indexOf(Object o,0)方法,indexOf内部就是简单的for循环遍历数组,若找到了返回元素的索引标号(Vector是允许元素重复的,这里返回的是第一个匹配的索引标号),否则返回-1。

5.2查找匹配元素最后1个索引号

前面说过,可能有多个元素匹配查找,若找找出最后一个匹配的索引号使用lastIndexOf方法:

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

和上述查找一样,但是数组遍历是从后往前的。

5.3其他查找方法

 //查找index位置的元素
 public synchronized E elementAt(int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
        }
        return elementData(index);
    }

    //查找第一个元素
    public synchronized E firstElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(0);
    }

    //查找最后一个元素
    public synchronized E lastElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(elementCount - 1);
    }

6主要操作

6.1添加元素

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

 private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }  

这里是在集合尾部添加一个元素。有以下几个操作:(1)modCount++;(2)判断是否需要扩容;(3)在集合尾部添加一个元素。

下面是在具体的索引位置添加1个元素:

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

 上述代码主要有以下几个操作:

(1)modCount++;

(2)判断索引位置是否越界;

(3)判断是否需要扩容;

(4)[index,elementCount]位置的数据复制到[index+1,elementCount+1];

(5)在index位置插入元素,然后elementCount加1

下面是将一个集合的所有元素添加到Vector集合里面:

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

这里说明一下toArray()方法,该方法是声明在Collection接口里面的,也就是说所有的Collection的实现类都应该有该方法的实现。我们看一下Vector是怎么实现的吧:

public synchronized Object[] toArray() {
        return Arrays.copyOf(elementData, elementCount);
    }

6.2删除元素

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)进行删除。

这里System.arraycopy(elementData, index + 1, elementData, index, j)是个native方法。把索引位后面的元素全部往前挪1位(底层是用复制实现的),最后1位不会改变,举个例子:

int[] a = { 1, 2, 3, 4, 5, 6 };
System.arraycopy(a, 3, a, 2, 3);

打印a得到{ 1, 2, 4, 5, 6 ,6}。所以源码最后1条要将数组末尾的值清空来方便GC。

删除指定位置的元素与上述removeElementAt方法类似:

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

删除Vector中指定集合的所有元素:

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

调用的是父类AbstractCollection中的removeAll方法:

public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }

这里的删除是通过迭代器实现的,AbstractCollection中并没有具体实现iterator(),所以这里Vector是调用自己的iterator()方法:

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

 private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            // Racy but within spec, since modifications are checked
            // within or after synchronization in next/previous
            return cursor != elementCount;
        }

        public E next() {
            synchronized (Vector.this) {
                checkForComodification();
                int i = cursor;
                if (i >= elementCount)
                    throw new NoSuchElementException();
                cursor = i + 1;
                return elementData(lastRet = i);
            }
        }

        public void remove() {
            if (lastRet == -1)
                throw new IllegalStateException();
            synchronized (Vector.this) {
                checkForComodification();
                Vector.this.remove(lastRet);
                expectedModCount = modCount;
            }
            cursor = lastRet;
            lastRet = -1;
        }

       //省略
    }

 Vector声明了一个内部类用来实现迭代器接口。具体的迭代器详情可以参考我的另一篇博客java 迭代器

补充:Vector可以通过listIterator()方法获得ListIterator对象(也是内部类实现),ListIterator接口继承了Iterator接口,不同的是前者可以双向遍历。

6.3get和set方法

get方法是取索引位置的元素,set方法是设置索引位置的元素:

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

        return elementData(index);
    }

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

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

 7总结

由于篇幅与实力的问题(主要是后者),这里只贴了一些常用方法的代码,其他详情以及Java8的新加入内容有待深入。

完。

posted @ 2018-04-18 09:24  OUYM  阅读(288)  评论(0编辑  收藏  举报