JDK源码阅读笔记之Vector


前言

现在是公元2018年,应该已经很少有人用Vector这个类了吧~反正我是没用过。作为一个从JDK 1.0版本就存在的元老级别成员,或许在一些远古级别的项目中还存在它的身影。但是还是止不住的好奇心,想对他的代码一探究竟,了解他的今世前生。

概述

Vector和ArrayList的功能是非常相似的。都是支持动态的扩充和减少数据。Vector可以线程安全的修改数据,但是也不支持在循环中修改数据,当发生这种情况的时候会抛出ConcurrentModificationException异常。Vector保有一个现在不推荐使用的循环结构Enumeration。这个类功能上已经被iterator代替了。很重要的一点就是Enumeration对循环中修改数据不会做检查,所以这会导致一些未定义的行为发生。从JDK 1.2版本开始,Vector实现了List接口,他也就是被归类到了Java集合框架中,在需要线程安全的场景中可以使用它。但是我们不推荐使用,因为相对于其他的线程安全的解决方案,Vector的线程安全是基于synchronized关键字来保证,对于synchronized这样的重型锁性能很有很大的影响,所以在性能敏感的项目中不推荐使用。

源码解读

继承类和接口

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

从代码可以看出,Vector继承了AbstractList类,同时实现了List接口。所以Vector是作为List接口的一个实现类,属于collection家族的医院。
实现RandomAccess,标示它具有随机访问的能力。
实现Cloneable,具有拷贝的能力(仅仅是浅拷贝)
实现java.io.Serializable,可以具有序列化的能力。

属性

  1. protected Object[] elementData 这个属性用来存储数据的。如果数据过多,就会进行扩容。
  2. protected int elementCount 这个属性用来标记Vector的元素数量。
  3. protected int capacityIncrement 这个属性用于扩充时,指定扩容大小

构造方法

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

initialCapacity决定了elementData初始化的大小,capacityIncrement决定了下一次扩容的大小。
Vector调用了父类的构造方法。其实父类的构造方法里面也是啥都没干。如果initialCapacity传入的是负数,那么不允许这种操作会抛出异常。
2.   Vector(int initialCapacity)

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

如上面的代码,这个重载构造方法内部调用的仍然是第一个构造方法,只不过将capacityIncrement设为0
3.   Vector()

  public Vector() {
        this(10);
    }

如果不传任何参数的构造方法,默认的initialCapacity大小为10, capacityIncrement为0

其他方法

1.   void copyInto(Object[] anArray)

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

这个方法类似copy方法,功能是将Vector的数据拷贝的传入的anArray里面。重点是抛出的几个异常。如果anArray是null,会抛出NullPointerException。如果anArray的空间不足会抛出IndexOutOfBoundsException异常。如果Vector数据的类型和anArray不兼容会抛出ArrayStoreException异常。实现是通过原生方法,所以不过多讲解。

2.   void trimToSize()

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

这个方法的作用是将Vector的空间和当前元素的数量一致。是一个减少空间大小的方法。

3. void ensureCapacity(int minCapacity)

   public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            if (minCapacity > elementData.length)
                grow(minCapacity);
        }
    }

4.  int capacity()

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

获取当前Vector的容量大小

5. int size()

    public synchronized int size() {
        return elementCount;
    }

获取当前当前Vector存储的数据大小

6. boolean isEmpty()

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

判断当前Vector是否为empty。

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

通过这个方法可以遍历Vector的所有元素。我们可以看到是通过内部实例化一个Enumeration类实现的功能。这个类功能和Iterator是完全一致的,并且可以转换成Iterator。现在遍历元素已经都通过Iterator提供了。Fail-fast机制,以及可选择的remove方法。Enumeration作为历史遗留的产物,很少使用他了。

8. contains(Object o)

   public boolean contains(Object o) {
        return indexOf(o, 0) >= 0;
    }

List集合的contains都是通过indexOf方法判断是否大于等于0来实现的。很好的实现了代码的复用。

9. indexOf(Object o)

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

indexOf方法是找到希望元素o在Vector中的位置。通过代码我们发现调用的是lingerie重载方法。

10. indexOf(Object o, int index)

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

这段代码在List类中基本上都是一样的。对于null元素在集合中查找第一个null元素。对于非null元素遍历查找第一个equal的元素。如果找不到就返回-1。

11. lastIndexOf(Object o)

   public synchronized int lastIndexOf(Object o) {
        return lastIndexOf(o, elementCount-1);
    }

功能就是indexOf反过来,从后面

12. lastIndexOf(Object o, int index)

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

整体逻辑和indexOf类似,但是是从后往前遍历。

13. E elementAt(int index)

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

        return elementData(index);
    }

这个方法就是获取对应位置的元素。逻辑上和get方法是等价的。

14. E firstElement()

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

获取Vector的第一元素。如果没有就抛出NoSuchElementException异常。这个不是显示异常,所以不用捕获。

15. E lastElement()

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

返回Vectro的最后一个元素。如果没有就抛出NoSuchElementException异常。

16. void setElementAt(E obj, int index)

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

在对应位置设置元素。逻辑上等同于set方法

17.removeElementAt(int index)

   public synchronized void removeElementAt(int index) {
        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);
        }
        modCount++;
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }

讲对应位置的元素从Vector中移除。为了提高效率List的移除操作基本上内部都使用 System.arraycopy直接将后续的元素拷贝过来。

18. void insertElementAt(E obj, int index)

 public synchronized void insertElementAt(E obj, int index) {
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index
                                                     + " > " + elementCount);
        }
        modCount++;
        final int s = elementCount;
        Object[] elementData = this.elementData;
        if (s == elementData.length)
            elementData = grow();
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = obj;
        elementCount = s + 1;
    }

19. void addElement(E obj)

 public synchronized void addElement(E obj) {
        modCount++;
        add(obj, elementData, elementCount);
    }
    
 private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        elementCount = s + 1;
    }    

20. boolean removeElement(Object obj)

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

删除指定元素,首先通过indexof方法获取obj的位置,然后通过removeElementAt讲对应位置的元素删除。

21. void removeAllElements()

    public synchronized void removeAllElements() {
        final Object[] es = elementData;
        for (int to = elementCount, i = elementCount = 0; i < to; i++)
            es[i] = null;
        modCount++;
    }

循环将Vector的所有元素都置为null。

22. Object clone()

  public synchronized Object clone() {
        try {
            @SuppressWarnings("unchecked")
            Vector<E> v = (Vector<E>) super.clone();
            v.elementData = Arrays.copyOf(elementData, elementCount);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

Vector的拷贝是浅拷贝。所以记得要自己实现深拷贝的方式。

23 其他方法

剩下的方法主要是和ArrayList类似的增删改查就不重复写了,另外为了让Vector兼容Java 8的lambda表达式还增加了一些特定的方法来支持。这部分之后打算在函数式编程中讲解。

总结

虽然Vecotor我们基本不使用了,但是还是可以从他的源码中学到一些东西。这样三个主要的List实现类就都讲完了,接下里我们会进入Map的源码分析中。