Vector源码分析

Vector源码分析

继承实现

public class Vector<E> 
extends AbstractList<E> 
implements List<E>, RandomAccess, Cloneable, Serializable
  • AbstractList继承了AbstractCollection抽象类,实现了最基本的集合的方法。大多数的集合都会继承此类或此类的派生类。
  • List接口定义了一个集合该有的一系列方法
  • RandomAccess,用于快速随机访问。空接口
  • Cloneable,实现了克隆方法。
  • Serializable,实现了序列化方法。

重要属性

// 元素个数
protected int elementCount;
// 存储数据的数组
protected Object[] elementData;
// 扩容增量
protected int capacityIncrement;
// 一开始数组的默认大小
private static final int DEFAULT_SIZE = 10;
  • 由此可见,Vector的核心实现就是顺序存储的线性表——数组

构造方法

    public Vector() {
        this(DEFAULT_SIZE, 0);
    }

    public Vector(int capacity) {
        this(capacity, 0);
    }

    public Vector(int capacity, int capacityIncrement) {
        if (capacity < 0) {
            throw new IllegalArgumentException("capacity < 0: " + capacity);
        }
        elementData = newElementArray(capacity);
        elementCount = 0;
        this.capacityIncrement = capacityIncrement;
    }

    public Vector(Collection<? extends E> collection) {
        this(collection.size(), 0);
        Iterator<? extends E> it = collection.iterator();
        while (it.hasNext()) {
            elementData[elementCount++] = it.next();
        }
    }
  • 前两个构造方法都是继续调用的第3个构造方法
  • 一般来说,第一个构造方法是调用者最常用的。不需要传任何参数。因为如果容量满了,Vector自动扩容
  • 因此,实际上第三个构造方法才是真正执行最多的!
  • 第四个构造方法,其实现一目了然。用迭代器将传来的集合中的元素一个一个赋值Vector的数组elementData
  • 第三个构造的newElementArray方法,实际上就是创建一个数组,返回引用。然后保存容量增量。如下:
    private E[] newElementArray(int size) {
        return (E[]) new Object[size];
    }

功能方法

add&insertElementAt

    @Override
    public void add(int location, E object) {
        insertElementAt(object, location);
    }

    @Override
    public synchronized boolean add(E object) {
        if (elementCount == elementData.length) {
            growByOne();
        }
        elementData[elementCount++] = object;
        modCount++;
        return true;
    }
  • 两个add方法
  • 第2个add方法是最常用的。
  • 第2个add方法。先判断容量是否已满,如果满了就会调用growByOne方法。
    /**
     * 确定扩容增量,进行数组的扩容
     */
    private void growByOne() {
        // 1.先判断容量增量是否<=0,如果无增量再判断数组的长度是否==0.
        // 2.如果连数组长度都等于0了,就说明这就是个空数组。给增量给1就行了。
        // 3.如果容量增量大于0,说明已经指定了此属性。到这里,已经确定增量多少合适!
        // 4.然后新建数组,新数组长度 = 原数组长度 + 合适的容量增量
        // 5.使用System.arraycopy将 原数组 数据 拷贝到 新数组 中
        // 6.最后将新数组引用赋值给 属性【elementData】
        int adding = 0;
        if (capacityIncrement <= 0) {
            if ((adding = elementData.length) == 0) {
                adding = 1;
            }
        } else {
            adding = capacityIncrement;
        }

        E[] newData = newElementArray(elementData.length + adding);
        System.arraycopy(elementData, 0, newData, 0, elementCount);
        elementData = newData;
    }
  • 据上述,可以知晓。growByOne就是进行数组扩容elementData已经是扩容后的新数组了。
  • 然后直接添加元素到数组中。
  • modCount就是个计数器,对于数组的修改,它都会++。这个不重要!
  • 而第1个add方法直接调用insertElementAt,见名知意。在指定位置插入元素。具体如下:
    // 在指定位置插入元素
    public synchronized void insertElementAt(E object, int location) {
        // 1.首先确保location在有效范围内
        if (location >= 0 && location <= elementCount) {
            // 2.如果容量已满,就进行数组扩容
            if (elementCount == elementData.length) {
                growByOne();
            }
            // 3.计算数组从location位置开始,后面的多少个元素需要后移一位
            int count = elementCount - location;
            if (count > 0) {
                // 4.进行数组的部分元素后移
                System.arraycopy(elementData, location, elementData,
                        location + 1, count);
            }
            // 5.指定位置插入元素
            elementData[location] = object;
            // 6.元素个数++
            elementCount++;
            modCount++;
        } else {
            throw arrayIndexOutOfBoundsException(location, elementCount);
        }
    }

小结

  • add方法可以看出,插入元素需要将该位置以后的所有元素后移效率不高!删除也应该如此。
  • 查询效率应该不错!因为是顺序存储。
  • Vectoradd方法加了synchronized线程安全。但因为有锁,因此效率降低

addAll

    @Override
    public synchronized boolean addAll(Collection<? extends E> collection) {
        return addAll(elementCount, collection);
    }

    @Override
    public synchronized boolean addAll(int location, Collection<? extends E> collection) {
        // 1.确保location的有效范围
        if (location >= 0 && location <= elementCount) {
            int size = collection.size();
            // 2.确保添加进来的集合不为null
            if (size == 0) {
                return false;
            }
            // 3.elementData.length - elementCount表示数组还剩多少个空余
            // size - (elementData.length - elementCount)表示加上添加进来的集合还需要多少个空余
            int required = size - (elementData.length - elementCount);
            if (required > 0) {
                // 4.如果确实需要空余,那么就进行数组扩容
                growBy(required);
            }
            // 5.需要让多少个元素后移
            int count = elementCount - location;
            if (count > 0) {
                // 6.进行后移
                System.arraycopy(elementData, location, elementData, location
                        + size, count);
            }
            Iterator<? extends E> it = collection.iterator();
            // 7.迭代器遍历,赋值到数组中间后移出来空余的部分
            while (it.hasNext()) {
                elementData[location++] = it.next();
            }
            // 8.元素增加!
            elementCount += size;
            modCount++;
            return true;
        }
        throw arrayIndexOutOfBoundsException(location, elementCount);
    }
  • addAll方法就是将数组从location开始到最后往后移动,移动collection.size()个。然后遍历把collection放进来。
  • 也是锁方法安全效率低

addElement

    public synchronized void addElement(E object) {
        // 1.如果容量已满,就进行数组扩容
        if (elementCount == elementData.length) {
            growByOne();
        }
        // 2.在数组最后添加元素
        elementData[elementCount++] = object;
        modCount++;
    }
  • 锁方法安全效率低

capacity

    // capacity容量的意思
    // 返回这个数组的容量,但记住不是元素的个数
    // 而是数组的容量。容量 >= 元素个数。
    public synchronized int capacity() {
        return elementData.length;
    }
  • 锁方法安全效率低
  • 注意容量和长度的区别

clear&removeAllElements

    @Override
    public void clear() {
        removeAllElements();
    }

    public synchronized void removeAllElements() {
        // 1.将所有的元素都置为null
        // 2.将元素个数设为0
        for (int i = 0; i < elementCount; i++) {
            elementData[i] = null;
        }
        modCount++;
        elementCount = 0;
    }
  • 数组的clear大多数都是这么做的,元素置为null,再将计数elementCount = 0.

clone

    @Override
    @SuppressWarnings("unchecked")
    public synchronized Object clone() {
        try {
            // 1.clone Vector的基本属性
            Vector<E> vector = (Vector<E>) super.clone();
            // 2.再将重要属性:数组,克隆一份
            vector.elementData = elementData.clone();
            // 3.clone一份相同的对象!
            return vector;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

index&contains

    @Override
    public boolean contains(Object object) {
        return indexOf(object, 0) != -1;
    }

    @Override
    public int indexOf(Object object) {
        return indexOf(object, 0);
    }

    // 查找object元素从location位置开始到最后,第一次出现的位置
    // 要查的object可以为null
    public synchronized int indexOf(Object object, int location) {
        if (object != null) {
            // 要查的数据不为null
            // 从location位置开始到最后,一个一个比较
            for (int i = location; i < elementCount; i++) {
                if (object.equals(elementData[i])) {
                    return i;
                }
            }
        } else {
            // 要查的数据为null
            // 从location位置开始到最后,一个一个比较
            for (int i = location; i < elementCount; i++) {
                if (elementData[i] == null) {
                    return i;
                }
            }
        }
        return -1;
    }
  • contains是查询集合中是否包含此元素indexOf是为了查询元素正序第一次出现的位置
  • 因此,contains可以利用indexOf满足需求。
  • indexOf的本质就是从头到尾挨个比较

containsAll

    @Override
    public synchronized boolean containsAll(Collection<?> collection) {
        return super.containsAll(collection);
    }
  • containsAll直接调用父类的containsAll方法,但不是AbstractList抽象类的,而是AbstractCollection抽象类的。
    // AbstractCollection.java
    public boolean containsAll(Collection<?> collection) {
        // 1.迭代器遍历collection的每一个元素
        // 2.只要collection集合中有一个元素不在当前集合中,就返回false
        Iterator<?> it = collection.iterator();
        while (it.hasNext()) {
            if (!contains(it.next())) {
                return false;
            }
        }
        return true;
    }

copyInto

    public synchronized void copyInto(Object[] elements) {
        // 将当前集合中的元素全部拷贝到elements数组中
        // 如果elements中有数据,会进行覆盖,覆盖elementCount个
        System.arraycopy(elementData, 0, elements, 0, elementCount);
    }
  • 将当前集合中的元素全部拷贝到另一个数组中。

elementAt

    @SuppressWarnings("unchecked")
    public synchronized E elementAt(int location) {
        if (location < elementCount) {
            // 返回指定位置的元素
            return (E) elementData[location];
        }
        throw arrayIndexOutOfBoundsException(location, elementCount);
    }

elements

    // Enumeration(枚举)接口的作用和Iterator类似,只提供了遍历Vector和HashTable类型集合元素的功能,
    // 不支持元素的移除操作。
    public Enumeration<E> elements() {
        return new Enumeration<E>() {
            int pos = 0;

            // 是否有下个元素
            public boolean hasMoreElements() {
                return pos < elementCount;
            }

            // 返回下个元素
            @SuppressWarnings("unchecked")
            public E nextElement() {
                synchronized (Vector.this) { // 锁
                    if (pos < elementCount) {
                        // 范围内,返回元素数据
                        return (E) elementData[pos++];
                    }
                }
                throw new NoSuchElementException();
            }
        };
    }
  • Enumeration用的不多了,因为和Iterator重叠的地方太多。且不如Iterator功能多。这个当作了解即可。

ensureCapacity&grow

    // 此方法的作用是:
    // 你给我指定想要扩容的大小
    // 我根据现有数组的使用情况,计算比较合适的扩容的大小
    // 两者比较,确定最终合适的大小
    // 然后调用grow进行扩容,扩容后的数组直接赋值给属性elementData
    public synchronized void ensureCapacity(int minimumCapacity) {
        if (elementData.length < minimumCapacity) {
            // 如果容量增量等于0,就选择数组长度,否则选择增量
            // 选择完毕后的增量,再加上数组长度 为 合适的增量
            int next = (capacityIncrement <= 0 ? elementData.length
                    : capacityIncrement)
                    + elementData.length;
            // 如果传来的指定的增量大于计算的合适的增量,就用指定的增量
            // 否则就用计算好的合适的增量
            grow(minimumCapacity > next ? minimumCapacity : next);
        }
    }

    private void grow(int newCapacity) {
        // 新建指定大小的数组
        E[] newData = newElementArray(newCapacity);
        // 拷贝所有数据到新数组中
        System.arraycopy(elementData, 0, newData, 0, elementCount);
        // 将新数组赋值给属性
        elementData = newData;
    }

growBy

    // 此方法的作用是:
    // 你传来一个你想要的数组的容量
    // 我经过一系列计算最终确定增量多少,然后新建数组,拷贝数据
    private void growBy(int required) {
        int adding = 0; // 数组的容量需要增加多少
        if (capacityIncrement <= 0) { // 如果增量为0
            if ((adding = elementData.length) == 0) { // 数组也是空的
                adding = required; // 增加为指定的量
            }
            // 如果数组不为空,让adding一直加数组.length
            // 直到adding >= 指定的量
            while (adding < required) {
                adding += adding;
            }
        } else { // 如果增量不为0
            // 反正也是一些计算
            adding = (required / capacityIncrement) * capacityIncrement;
            if (adding < required) {
                adding += capacityIncrement;
            }
        }
        // 最终确定数组需要的容量
        // 新建数组
        // 拷贝数据
        // 赋值属性
        E[] newData = newElementArray(elementData.length + adding);
        System.arraycopy(elementData, 0, newData, 0, elementCount);
        elementData = newData;
    }
  • growByOne类似,外界很少调用。都是内部调的。理解意思即可。

isEmpty

    @Override
    public synchronized boolean isEmpty() {
        return elementCount == 0;
    }
  • isEmpty,比较的是元素个数而不是数组长度。这一点一定要注意!

lastElement

    @SuppressWarnings("unchecked")
    public synchronized E lastElement() {
        try {
            return (E) elementData[elementCount - 1];
        } catch (IndexOutOfBoundsException e) {
            throw new NoSuchElementException();
        }
    }
  • elementCount是元素个数,索引从0开始。所以最多到elementCount-1

lastIndexOf

    @Override
    public synchronized int lastIndexOf(Object object) {
        return lastIndexOf(object, elementCount - 1);
    }

    public synchronized int lastIndexOf(Object object, int location) {
        // 1.确保location的有效范围
        if (location < elementCount) {
            if (object != null) { // 如果要查询的元素不为null
                for (int i = location; i >= 0; i--) { // 从后往前遍历比较
                    if (object.equals(elementData[i])) {
                        return i; // 找到就返回索引位置
                    }
                }
            } else { // 如果要查询的元素为null,null也可以作为一种元素存储
                for (int i = location; i >= 0; i--) { // 从后往前遍历比较
                    if (elementData[i] == null) {
                        return i; // 找到就返回索引位置
                    }
                }
            }
            return -1;
        }
        throw arrayIndexOutOfBoundsException(location, elementCount);
    }
  • indexOf正序查找lastIndexOf逆序查找。但查找内容一样。

remove&removeElement&removeElementAt

    @SuppressWarnings("unchecked")
    @Override
    public synchronized E remove(int location) {
        if (location < elementCount) { // 确保location的有效范围
            E result = (E) elementData[location]; // location位置上的数据
            elementCount--; // 有效元素个数--
            int size = elementCount - location; // 需要偏移的元素的个数
            if (size > 0) {
                // 进行偏移,删除一个,所以往前偏移
                System.arraycopy(elementData, location + 1, elementData,
                        location, size);
            }
            elementData[elementCount] = null; // 最后一位置为null
            modCount++;
            return result;
        }
        throw arrayIndexOutOfBoundsException(location, elementCount);
    }

    @Override
    public boolean remove(Object object) {
        return removeElement(object);
    }

    public synchronized boolean removeElement(Object object) {
        int index;
        if ((index = indexOf(object, 0)) == -1) { // 用正序indexOf查找到元素的索引
            return false;
        }
        // 调用方法进行删除
        removeElementAt(index);
        return true;
    }

    // 删除指定位置上的元素
    public synchronized void removeElementAt(int location) {
        if (location >= 0 && location < elementCount) { // 确保location的有效范围
            elementCount--; // 有效元素个数--
            int size = elementCount - location; // 需要偏移的元素的个数
            if (size > 0) {
                // 进行偏移,删除一个,所以往前偏移
                System.arraycopy(elementData, location + 1, elementData,
                        location, size);
            }
            elementData[elementCount] = null; // 最后一位置为null
            modCount++;
        } else {
            throw arrayIndexOutOfBoundsException(location, elementCount);
        }
    }
  • 关键方法remove(int location)removeElementAt(int location)方法内容何其相似。甚至注释都不用改!

removeRange

    @Override
    protected void removeRange(int start, int end) {
        // 确保范围的有效性
        if (start >= 0 && start <= end && end <= elementCount) {
            if (start == end) { // 开始和结束相等无意义
                return;
            }
            if (end != elementCount) { // end最多可以到达elementCount - 1
                // 拷贝数据从end到最后,拷贝到start开始,一共拷贝elementCount-end个
                System.arraycopy(elementData, end, elementData, start,
                        elementCount - end);
                // 设:elementCount = 10, start = 3, end = 5
                // newCount = 8
                // 8 - 10 fill null ==> 8, 9 设为null
                int newCount = elementCount - (end - start);
                Arrays.fill(elementData, newCount, elementCount, null);
                elementCount = newCount;
            } else { // 如果end等于elementCount,这里做容错处理,将数组从start开始往后的所有元素都填充为null
                Arrays.fill(elementData, start, elementCount, null);
                elementCount = start;
            }
            modCount++;
        } else {
            throw new IndexOutOfBoundsException();
        }
    }
  • 范围删除,再arraycopy的时候就已经删除了
  • 后面是将最后多余的元素置为null

retainAll

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

    // AbstractCollection.java
    public boolean retainAll(Collection<?> collection) {
        boolean result = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            // 当前集合中如果有collection集合不包含的元素,那么就删除
            if (!collection.contains(it.next())) {
                it.remove();
                result = true;
            }
        }
        return result;
    }
  • 一样的retainAll调用的是父类的父类的retainAll方法
  • 取交集,当前集合中只能存在collection集合中包含的,collection集合不包含的元素。当前集合要全部删除

set

    @SuppressWarnings("unchecked")
    @Override
    public synchronized E set(int location, E object) {
        if (location < elementCount) { // 确保location的有效范围
            E result = (E) elementData[location]; // location位置上的数据
            elementData[location] = object; // 覆盖location位置上的数据
            return result;
        }
        throw arrayIndexOutOfBoundsException(location, elementCount);
    }

setElementAt

    public synchronized void setElementAt(E object, int location) {
        if (location < elementCount) { // 确保location的有效范围
            elementData[location] = object; // 设置指定位置上的数据
        } else {
            throw arrayIndexOutOfBoundsException(location, elementCount);
        }
    }

setSize&size

    public synchronized void setSize(int length) {
        // 如果指定长度和现有元素个数相同,就没必要设置了
        if (length == elementCount) {
            return;
        }
        // 进行扩容量的判定,判定完进行扩容
        ensureCapacity(length);
        if (elementCount > length) {
            // 如果元素个数大于数组长度,就将剩余的数组元素填充为null
            Arrays.fill(elementData, length, elementCount, null);
        }
        elementCount = length; // 刷新元素个数
        modCount++;
    }

    @Override
    public synchronized int size() {
        return elementCount;
    }
  • setSize,设置集合长度
  • 不能想设置多少就设置多少,所以关键方法ensureCapacity会进行计算和判定
  • 判定完再扩容

总结

  • Vector中的方法基本过了一遍,跟ArrayList很类似。毕竟都是以数组为核心存储数据
  • Vector也经常和ArrayList进行比较,比较结果:
  • Vector安全,但效率低ArrayList不安全,但效率高
  • ArrayList就是为了解决Vector效率低而出的产物。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值