• 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的新加入内容有待深入。

    完。

  • 相关阅读:
    $$Ctrl+shift+M分数罗马字母小写
    设置字体大小加粗斜体下划线
    无序列表有序列表
    数学公式插入
    文本居中、代码插入、图片插入
    VM 使用问题 | 安装失败->>注册表
    VM小技巧——虚拟机解决vm窗口太小的办法
    OneNet平台实践
    linux 打包 | autoconf 使用方法
    C 总结 | 复习注意点
  • 原文地址:https://www.cnblogs.com/ouym/p/8872007.html
Copyright © 2020-2023  润新知