JDK类库源码分析系列3--集合类分析(5) 集合4-Vector&Stack

150 阅读2分钟

一、Vector

1、Vector的结构

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

​ 我们知道Vector与ArrayList比较其是线程安全的,我们可以看到这个与ArrayList一样实现了RandomAccess(因为其也是通过数组实现)。

2、成员变量

1)、elementCount

protected Object[] elementData;

​ 这个与ArrayList的size类似,就是用来表明有多少个元素的。

2)、elementData

protected Object[] elementData;

​ 这个就是用来放元素的数组

3)、capacityIncrement

protected int capacityIncrement;

​ 这个应该是ArrayList没有的这个变量,这个成员变量是表明在扩容的时候扩大多少(ArrayList

一般是默认原来的1.5)。

3、构造方法

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

2)、Vector(int initialCapacity)

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

​ 这里是调用1的方法,不过capacityIncrement默认传的是0。

3)、Vector()

public Vector() {
    this(10);
}

​ 这个又是对第一个构造函数的补充默认,其的initialCapacity初始化数组的大小是10(ArrayList的DEFAULT_CAPACITY也是10)。其还有一个初入Collection去初始化创建一个Vector(Vector(Collection<? extends E> c))

4、主要方法

​ 我们在前面有讲过,Vector与ArrayList主要不同点是Vector是线程安全的,这是因为其暴露出来供使用的方法都是加了synchronized来同步的,所以其是线程安全的。但我们知道synchronized是一种很重的锁,所以JDK提供了concurrent包,来减少对应的颗粒度,这个我们到时候再来梳理学习。

1)、trimToSize()

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

​ 这个方法就是缩减elementData数组的长度,将其变为elementCount长度的数组,来减少没有使用到的浪费的空间。

2)、grow(int minCapacity)

private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}

​ 这个就是根据一个最小的容量minCapacity去扩容创建copy一个新数组,不过可以看到其主要工作的是newCapacity(minCapacity)方法。

3)、newCapacity

private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity <= 0) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

​ 这个其实与ArrayList的类似,特别是在最后面的一个return的逻辑。但这里主要的扩容逻辑还是不同,这里的扩容是判断capacityIncrement(可以自己设置)有没有主动设置,如果有,则是再增加capacityIncrement的长度,如果没有则是则是原来双倍的容量(不是像原来ArrayList一样是向右移一位)。

这个Vector其他的一些方法增删改查大部分与ArrayList是类似的,这里就不再梳理了,主要是在前面加了synchronized字段。

二、ArrayDeque

​ 这个是用数组的形式对Deque接口的方法实现,类似与上一篇文章用链表的形式对Deque接口的实现。

1、结构

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable

​ 可以看到其是继承了AbstractCollection类。

2、变量

1)、elements

transient Object[] elements;

​ 存放元素的数组。

2)、head

transient int head;

​ 队首元素的位置。

3)、tail

transient int tail;

​ 队尾元素的位置。

3、构造方法

1)、ArrayDeque()

public ArrayDeque() {
    elements = new Object[16];
}

​ 可以看到其的初始化数组大小是16。

2)、ArrayDeque(int numElements)

public ArrayDeque(int numElements) {
    elements =
        new Object[(numElements < 1) ? 1 :
                   (numElements == Integer.MAX_VALUE) ? Integer.MAX_VALUE :
                   numElements + 1];
}

​ 这个是根据入参numElements来决定大小的,然后小于1,则用1替代。

​ 同样其也有Collection形式的构造方法ArrayDeque(Collection<? extends E> c)。

三、Stack

这个有名称也可以看到其是栈结构,同时其的方法是线程安全的。

1、结构&构造方法

public
class Stack<E> extends Vector<E> {
    public Stack() {
    }

​ 我们可以看到其是直接继承的继承Vector。

2、方法

1)、push(E item)

public E push(E item) {
    addElement(item);

    return item;
}

​ 这个是push添加元素的方法,其直接调用的Vector的addElement(item)方法。

2)、pop()

public synchronized E pop() {
    E       obj;
    int     len = size();

    obj = peek();
    removeElementAt(len - 1);

    return obj;
}

​ 这个是出栈,然后也是调用的removeElementAt方法。同时可以看到这个是线程安全的。这个是出栈操作,所以其会将现在该位置的内容删除调。

3)、peek()

public synchronized E peek() {
    int     len = size();

    if (len == 0)
        throw new EmptyStackException();
    return elementAt(len - 1);
}

​ 这个方法也是获取栈顶的日弄,不过可以看到其是调用的elementAt方法,这个方法就是获取该位置的值,而并不会删除栈顶的值。