源码阅读之Vector

源码阅读是基于JDK7,本篇主要涉及Vector常用方法源码分析。

1.概述
Vector实现了一个增长型的Object数组,可以包含任何类型的元素,包括null。像数组一样,它的元素可以使用下标索引值进行访问。为了容纳添加或删除后的元素,Vector的容量可以增长或收缩。每个Vector实例通过维护容量大小和容量增长因子来优化存储管理。Vector容量的大小要大于等于Vector中元素的实际个数,如果添加元素时需要扩容,则会按照增长因子的大小进行扩容。当需要添加大量的元素到Vector中,需要给它进行合适的扩容,这样可以减少增量式的内存再分配次数。像ArrayList一样,通过Vector的iterator方法和listIterator方法返回iterators时被设计成是fail-fast,当调用这两个方法的时候,如果Vector实例被从结构上进行了修改(指添加或删除一个或多个元素的操作,除了ListIterator的remove方法或add方法,或者显式调整底层数组的大小,仅仅设置元素的值不是结构上的修改),将会抛出ConcurrentModificationException,这样的设计避免了某个不确定的时间因为修改而出现的不可预知的问题。需要注意的是这个类的elements方法并不是fail-fast。Vector和ArrayList大体上的相同的,不同的是ArrayList是非同步的,而Vector是同步的,其实就是在实现的方法上使用synchronized进行同步。Vector具有同步的特性,是线程安全的,所以它的性能相对于ArrayList来说是低效的,因为同步操作需要消耗时间,针对于不需要使用线程安全的情况来说,推荐使用ArrayList代替Vector。Vector实现了RandomAccess接口可以进行快速的随机访问。Vector实现了Cloneable接口可进行clone操作。Vector实现了java.io.Serializable接口,可进行序列化操作。

2.数据结构
Vector类的声明代码如下,

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

由上面的代码段可以得出如下所示的类图,

Paste_Image.png

Vector中声明了如下一些属性,

protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement;
protected transient int modCount = 0;

(1)elementData数组用于存储元素;
(2)elementCount为Vector中实际元素个数;
(3)capacityIncrement为Vector扩容的增长因子;
(4)modCount从AbstractList继承而来,用于记录从结构上修改此Vector的次数;

3.构造方法
提供了四个构造函数可供使用。

    //根据指定初始容量大小、容量增长因子构造一个空的Object数组
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

    //根据指定初始容量大小、容量增长因子为零构造一个空的Object数组
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    //初始容量大小为10、容量增长因子为0构造一个空的Object数组
    public Vector() {
        this(10);
    }

    //将Collection中的元素按照迭代次序存入elementData中
    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.扩容
当添加元素的时候,检查数组容量是否需要扩容。

    //使用synchronized进行同步
    public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            ensureCapacityHelper(minCapacity);
        }
    }

    //指定的扩容大小大于elementData数组的长度才进行扩容
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    //可分配的最大数组大小,因为一些VM需要在数组中预留字节头,所以需要减8
    //尝试去分配的数组大小超过了VM的限制会报OutOfMemoryError
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    //从这里可以得出尝试扩容的大小可能是原容量加上增长因子的大小或原容量的二倍
    //扩容后最大容量大小是Integer.MAX_VALUE
    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;
    }

5.setSize方法

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

设置容量大小为newSize。如果newSize大于当前的容量,则会进行扩容;否则,从newSize开始到末尾的所有元素会被丢弃,被设置为null。

6.capacity方法

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

返回当前Vector的容量大小,是线程安全的。

7.size方法

    public synchronized int size() {
        return elementCount;
    }

返回当前Vector中元素的实际个数。

8.isEmpty方法

    public synchronized boolean isEmpty() {
        return elementCount == 0;
    }

判断当前Vector中元素的书籍个数是否等于零。

9.elements方法

    public Enumeration<E> elements() {
        return new Enumeration<E>() {
            int count = 0;

            public boolean hasMoreElements() {
                return count < elementCount;
            }

            public E nextElement() {
                synchronized (Vector.this) {
                    if (count < elementCount) {
                        return elementData(count++);
                    }
                }
                throw new NoSuchElementException("Vector Enumeration");
            }
        };
    }

返回一个Enumeration实例,用于遍历Vector。

10.contains方法

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

从前向后遍历elementData数组,匹配第一个和o相等的元素,如果存在返回对应元素的下标序号,否则返回-1。这里需要注意的是o.equals(elementData[i]),这里是Objetc的equals方法,比较的是引用。

11.indexOf方法

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

从前向后查找匹配的第一个元素,若存在返回对应的索引下标,否则返回-1。

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

从后向前查找匹配的第一个元素,若存在返回对应的索引下标,否则返回-1。

13.elementAt方法

    public synchronized E elementAt(int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
        }

        return elementData(index);
    }

返回指定索引下标位置的元素。

14.firstElement方法

    public synchronized E firstElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(0);
    }

返回第一个元素。

15.lastElement方法

    public synchronized E lastElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(elementCount - 1);
    }

返回最后一个元素。

16.setElementAt方法

    public synchronized void setElementAt(E obj, int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        elementData[index] = obj;
    }

17.removeElementAt方法

    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;//GC回收
    }

18.insertElementAt方法

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

19.addElement方法

    public synchronized void addElement(E obj) {
        modCount++;
        //扩容判断及操作
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

20.removeElement方法和removeAllElements方法

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

    public synchronized void removeAllElements() {
        modCount++;
        // Let gc do its work
        for (int i = 0; i < elementCount; i++)
            elementData[i] = null;

        elementCount = 0;
    }

21.toArray方法

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

该方法返回一个新的数组,数组中的元素按照Vector中的第一个到最后一个顺序排列,修改返回的数组不会影响原Vector中的数据。

22.get方法

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

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

        return elementData(index);
    }

23.set方法

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

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

set方法就是给数组的指定下标成员重新赋值。

24.add方法

    //首先进行扩容判断
    //给数组的elementCount++位置赋值为e
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

    //给index下标成员重新赋值
    public void add(int index, E element) {
        insertElementAt(element, index);
    }

25.remove方法

    //删除指定位置的元素
    //原理:将index+1位置开始到末尾的元素向前移动一位,最后一位赋值为null,GC进行回收
    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;
    }

    //删除指定内容的元素,删除匹配的第一个元素
    //原理:将匹配的第一个元素的位置下标加1开始到末尾的元素向前移动一位,最后一位赋值为null,GC进行回收
    public boolean remove(Object o) {
        return removeElement(o);
    }

26.clear方法

    public void clear() {
        removeAllElements();
    }

将数组所有元素引用设置了null,Vector的元素个数置为0;

27.iterator方法和listIterator方法
这两个方法同ArrayList中两个方法,不同点是进行了同步操作。在执行iterator或listIterator方法时,如果有线程从结构上做了修改(指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小,仅仅设置元素的值不是结构上的修改),这两个方法会fail-fast,将会抛出ConcurrentModificationException。迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败操作会尽最大努力抛出ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException应该仅用于检测bug。抛异常就是为了避免数据问题而存在的,其实就是检查modCount是否是期望值,如果不是,则说明Vector数组被从结构上修改了。这里是经常会遇到的问题,使用Java的For Each方式遍历元素时删除匹配的元素,会抛出ConcurrentModificationException。Java中的For Each实际上使用的是iterator进行处理的,而iterator是不允许集合在iterator使用期间删除的。

小结:
1.Vector底层也是基于动态数组的数据结构。
2.Vector在每次增加元素时,都要进行扩容判断,扩容时都要确保足够的容量。当容量不足以容纳当前的元素个数时,就设置新容量为旧的容量的2倍或旧容量加增长因子大小,如果新容量还不够,则新容量为最大容量。从中可以看出,当容量不够时,都要将原来的元素拷贝到一个新的数组中,耗时而且还需要重新分配内存,因此建议在事先能确定元素数量的情况下,明确指明容量大小。
3.Vector基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,效率低。
4.Vector是线程安全的,方法会进行同步,相对于ArrayList来说效率相对低,建议在非同步的情况下使用ArrayList。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容