Vector & Stack深入源码

Java容器专栏: Java容器源码详细解析(面试知识点)

因为部分内容可能已经在本专栏前面的文章提及到,这里只是简单说明,所以建议先看看:

Vector

Vector这个类现在已经不常用了,快被遗弃了。

(一)Vector底层数据结构

和ArrayList一样,底层都是Object动态数组。(向量队列)

(二)Vector继承和实现关系

(和ArrayList一样,在ArrayList中已有说明,这里不做过多的解释)

1、实现的接口:

       List接口   、  Cloneable接口   、  Serializable接口   、  RandomAccess接口

2、继承的抽象类:

       AbstractList抽象类

(三)Vector源码分析

1、重要的成员变量

//存储元素的数组,向量中最后一个元素后面的任何元素都是null
protected Object[] elementData;

//实际元素个数
protected int elementCount;

//容量增长系数:每次需要增长时,值增加一倍。
protected int capacityIncrement;

//序列化ID,保证序列化版本一致
private static final long serialVersionUID = -2767605614048989439L;

2、四个构造方法

//创建默认的Vector(默认初始容量为10,增长系数为0)
public Vector() {
    //调用下面的构造方法
    this(10);
}

//创建指定初始容量的Vector,增长系数为0
public Vector(int initialCapacity) {
    //调用下面的构造方法
    this(initialCapacity, 0);
}

//创建指定初始容量和增长系数的Vector
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    //初始容量不能为0
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

//创建由指定Collection生成的Vector
public Vector(Collection<? extends E> c) {
    elementData = c.toArray();
    elementCount = elementData.length;
    //c.toArray有可能不会返回object[],所以下面增加一道保险
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

3、获取元素操作

//没啥好说,和ArrayList基本一致,就加上了个synchronized 保证同步
public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}

4、添加元素操作

//同步添加元素
public synchronized boolean add(E e) {
    modCount++;
    //每次添加都需要确认数组的容量大小(或有空间无操作,或是扩容)
    ensureCapacityHelper(elementCount + 1);
    //添加元素
    elementData[elementCount++] = e;
    return true;
}

//确保数组容量充足(minCapacity为可满足的最小容量)
private void ensureCapacityHelper(int minCapacity) {
    //如果实际容量 < 可满足最小容量
    if (minCapacity - elementData.length > 0)
        //扩容
        grow(minCapacity);
}

//扩容操作(和ArrayList没差,就是扩容的大小有点区别)
private void grow(int minCapacity) {
    //获取原容量
    int oldCapacity = elementData.length;
    //进行扩容:
    //如果扩容系数不为0,则直接扩容此系数大小的空间
    //如果扩容系数为0,则直接扩容原容量大小(即增加一倍)
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    //扩容后还是不满足最小容量要求                                 
    if (newCapacity - minCapacity < 0)
        //就直接扩容到最小可满足容量
        newCapacity = minCapacity;
    
    //如果扩容后,大于数组的最大长度
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //调用此方法设置合适的容量大小
            //若最小满足容量小于MAX_ARRAY_SIZE,则扩容到MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)
            //若最小满足容量已经大于MAX_ARRAY_SIZE了,则扩容到Integer.MAX_VALUE(0x7fffffff)
        newCapacity = hugeCapacity(minCapacity);
    //最后将原数组中的数据复制到大小为 newCapacity 的新数组中,并将新数组赋值给 elementData。    
    elementData = Arrays.copyOf(elementData, newCapacity);
}

总结一下添加和扩容机制

无论调用哪个方法进行元素添加,一定会先调用ensureCapacityHelper确保数组的容量足够在进行元素的添加。

在ensureCapacityHelper中判断是否需要扩容,若需要扩容,则调用grow进行扩容。

扩容时,先看容量增长系数CapacityIncrement是否为0

         a)如果不为0,新容量 = 旧容量 + 增长系数

         b)如果为0,新容量 = 旧容量 + 旧容量(即翻1倍)

若上面扩容后还不够,则新容量 = 传入的参数minCapacity(即可满足的最小容量)

再和MAX_ARRAY_SIZE比较(具体看上面源码注释),得到最终的扩容结果

最后后用Arrays.copyof()方法将元素拷贝到新的数组。

5、删除元素操作

//同样和ArrayList差不多,就只是加上synchronized
//不过Vector也提供了很多其他的删除方法:(功能看方法名就知道)
removeElementAt(int)、removeElement(Object)、removeAllElements()

6、其他方法

//设置Vector的元素个数
public synchronized void setSize(int newSize) {
    //修改次数+1
    modCount++;
    //如果传进来的新size大小要大于当前的元素个数
    if (newSize > elementCount) {
        //则选择该值进行扩容操作
        ensureCapacityHelper(newSize);
    } else {
        //否则,则将下标为[newSize,elementCount-1]的元素都设置为null
        for (int i = newSize ; i < elementCount ; i++) {
            elementData[i] = null;
        }
    }
    elementCount = newSize;
}
//获取当前元素个数
public synchronized int size() {
    return elementCount;
}

//获取当前数组容量
public synchronized int capacity() {
    return elementData.length;
}

(三)线程安全性

是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),在增删改查方法上都加了synchronized修饰符,保证同步。

但是由于保证了线程安全,需要不断加锁,性能上也就相对较低。

注意:Vector中,加synchronized的方法都是public、protected方法(不是所有的public方法都加的),而像private这种仅被其他public方法调用的方法,并不需要加上synchronized

(四)克隆机制

浅克隆,同样比ArrayList多加了synchronized

(五)序列化机制

Vector中只有writeObject方法,没有readObject方法(所以序列化使用自定义的writeObject,反序列化使用默认的方法)

 

————————————————————————————————————————————————————————————

Stack

(一)概述

底层也是数组继承了Vector类。(也仅仅直接继承Vector类,没有继承其他类,也没有直接实现其他接口)

(二)源码

//下面就是Stack类所有的源码
//各种方法其实都是调用Vector的相关方法,只不过对其封装成栈相关的方法
public class Stack<E> extends Vector<E> {
    //创建一个空栈
    public Stack() {
    }

    //入栈(不断在数组末尾加入,即栈顶是在数组末)
    public E push(E item) {
        addElement(item);
        return item;
    }

   //出栈
    public synchronized E pop() {
        E obj;
        //获得栈长度
        int len = size();

        obj = peek();
        //根据下标出栈
        removeElementAt(len - 1);

        return obj;
    }

    //查看栈顶(数组最后一个元素)
    public synchronized E peek() {
        int     len = size();

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

    //判空
    public boolean empty() {
        return size() == 0;
    }

    //从栈顶(数组末)开始查找,返回找到的第一个元素的下标
    public synchronized int search(Object o) {
        int i = lastIndexOf(o);

        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }

    private static final long serialVersionUID = 1224463164541339165L;
}

栈的实现不仅仅只能通过数组来实现,也可以使用链表来实现

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值