美文网首页java源码学习
Java源码学习--Vector

Java源码学习--Vector

作者: 慕北人 | 来源:发表于2019-06-01 17:28 被阅读0次

    Java源码学习--Vector

    AbstractList的实现类我们常见的就是ArrayList和LinkedList两个,但是Vector也是其一个实现类,我几乎没有使用过这个类,这次就一并来学习一下其源码,看看其和另外两个出名的List相比有何区别。

    一、重要属性

    Vector的属性和ArrayList相比少了一些:

    静态属性

    • serialVersionUID:是一个final修饰的long类型,和ArrayList相同,在序列化和反序列化的时候使用

    普通属性

    • elementData:是一个Object[]类型的对象,其作用和ArrayList中的同名对象一样
    • elementCount:是一个int类型的数据,其和ArrayList中的size属性作用一样
    • capacityIncrement:是一个int类型的数据,这个属性是当elementCount的值到达了elementData的容量之后,进行扩容时扩大的容量,当这个值小于等于0时扩容后的容量为当前容量的二倍

    二、构造器

    Vector的构造器和ArrayList相比,逻辑简单了很多,如果您的记性好的话,应该会记得ArrayList中有两个属性EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA,而他俩的爱恨纠葛在ArrayList的不同的构造器重载以及后续一些方法的调用中都有体现。Vector则简单直白了很多,因为它没有这些花里胡哨的玩意。

    Vector的构造器中只有两个是最终被调用的。

    第一个

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

    可以看到,平平无奇,朴素至极,就是初始化了elementData和capacityIncrement两个属性

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

    可见这两个构造器都是回调的含两个参数的构造器。

    第二个

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

    第二个构造器只不过是使用参数中的Collection来初始化属性。

    三、重要方法

    1. add

    作为一个容器,其add方法是最常用的方法:

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

    这里的ensureCapacityHelper方法是用来检查是否需要扩容的(细节到后面再说),add方法可见就是一个数组的赋值操作,并无任何特点。

    add(int index, E element)

    该方法将元素添加到指定的位置:

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

    可以看到insertElementAt中首先也要判断是否需要扩容;之后就是将index后面的元素通过System.arraycopy方法实现向后移动一个元素的位置;最后将需要添加的元素插入到对应的位置。该方法由于需要将index后面的元素整体移动一格,所以相对来说还是有一些耗时的。

    addAll(Collection c)

    有了前面两个方法的铺垫之后,addAll的登场便显得没那么神秘了:

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

    也是老套路先检查是否需要扩容;之后进行数据的批量添加,同样耗时点在于System.arraycopy

    addElement

    可能是由于本人太菜了,一直不能理解该方法和add方法同时存在的意义:

    public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }  
    

    没错,你没有看错,其只是比add方法少了一个return ture

    2. remove

    remove方法和add方法是一对儿,也是容器类中必备的方法:

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

    可以看到,remove方法比add方法要麻烦一点,光是找到需要remove掉的对象就需要一个for循环一个一个地找,而之后调用的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; /* to let gc do its work */
    }  
    

    该方法唯二要做的就是把index后面的元素前移动一格;将最后一个元素设置为null

    remove(int index)

    该方法和removeElementAt方法的实现是一模一样的,只是多了一个return语句:

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

    removeAllElements

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

    该方法很简单,就是一个for循环将所有的元素赋值为null。

    3. set、get方法

    set方法和get方法也是一对,分别用来修改和获取数据:

    public synchronized E set(int index, E element) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
    
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }  
    
    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
    
        return elementData(index);
    }    
    

    4. 其他方法

    public synchronized void copyInto(Object[] anArray) {
        System.arraycopy(elementData, 0, anArray, 0, elementCount);
    }  
    

    简单粗暴,直接调用System.arraycopy方法将容器内的元素拷贝到参数中。

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

    很简单的功能:size大于元素个数的时候检查是否需要扩容;size小于元素个数的时候将超出size的置为null(注意虽然setSize此时的参数size更小,但是elementData属性的length是不会变小的,只是elementCount变小了,也即只存在扩容操作,不存在缩容的情况)

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

    这个方法印象中ArrayList是没有提供的,该方法返回的是elementData的长度,其大于等于elementCount(size()方法的返回值)

    public boolean contains(Object o) {
        return indexOf(o, 0) >= 0;
    }  
    
    public int indexOf(Object o) {
        return indexOf(o, 0);
    }  
    
    public synchronized int lastIndexOf(Object o) {
        return lastIndexOf(o, elementCount-1);
    }  
    

    这三个方法也是形影不离的,很简单,都是for循环搞定。

    5. 令人费解的方法

    唉,又是一堆方法,个人感觉有些已有的方法和这些都已经重复了,但是不知道为何还要保留出来:

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

    该方法和get方法完全一样

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

    这两个方法分别等价于get(0)与get(size())

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

    该方法和set方法几乎一模一样,只是set方法需要返回修改前的数据

    四、迭代器

    Vector和ArrayList很想,其也有两个方法来获取迭代器:

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

    当然类比ArrayList不用想也知道Itr是ListItr的直接父类,子类相较于父类,添加了很多方法(内部维护了modCount一致)来提供更多的功能

    1. Itr

    Vector的Itr的实现和ArrayList的差不多,三个属性都是一模一样的,直接copy来:

    • cursor:是一个int值,代表当前所在的位置,其范围是[0, elementCount]
    • lastRet:是一个int值,默认为-1,代表最后一次调用next方法返回的数据的位置
    • expectedModCount:为一个int值,初始化时赋值为了Vector的modCount

    重要方法

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

    如果你不限麻烦的话,可以发现和ArrayList相比,这几个方法的内容几乎是一模一样的,只是使用了synchronize修饰了一番。

    2. ListItr

    final class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            super();
            cursor = index;
        }
    
        public boolean hasPrevious() {
            return cursor != 0;
        }
    
        public int nextIndex() {
            return cursor;
        }
    
        public int previousIndex() {
            return cursor - 1;
        }
    
        public E previous() {
            synchronized (Vector.this) {
                checkForComodification();
                int i = cursor - 1;
                if (i < 0)
                    throw new NoSuchElementException();
                cursor = i;
                return elementData(lastRet = i);
            }
        }
    
        public void set(E e) {
            if (lastRet == -1)
                throw new IllegalStateException();
            synchronized (Vector.this) {
                checkForComodification();
                Vector.this.set(lastRet, e);
            }
        }
    
        public void add(E e) {
            int i = cursor;
            synchronized (Vector.this) {
                checkForComodification();
                Vector.this.add(i, e);
                expectedModCount = modCount;
            }
            cursor = i + 1;
            lastRet = -1;
        }
    }  
    

    唉,偷个懒,可以发现相较于其直接父类Itr,增加了previous、add、set三个方法;其细节和注意点和Itr是一以贯之的。

    五、总结

    可以看到Vector和ArrayList几乎是一个模子里刻出来的,Vector唯一能拿的出手的好像只有一点:其是线程安全的,这得益于其所有方法(或者是内部的代码块)都是用了synchronize修饰了的,但是这种简单粗暴的实现也宣告着Vector在高并发情况下的表现也是不尽如人意的。

    相关文章

      网友评论

        本文标题:Java源码学习--Vector

        本文链接:https://www.haomeiwen.com/subject/fsmatctx.html