Java容器(四):List 之 Vector 和 Stack源码分析

Java容器

集合框架

二、List

List:可重复、有序(存取顺序相同)

List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。

2.3 Vector

2.3.1 类结构

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

Vector 和 ArrayList 相似,其内部都是通过一个容量能够动态增长的数组来实现。

  • Vector 继承了 AbstractList,实现了List接口。
  • Vector 实现了 RandmoAccess接口,即提供了随机访问功能。
  • Vector 实现了 Cloneable接口,即实现克隆功能。
  • Vector 实现了 Serializable接口,表示支持序列化。

2.3.2 属性

//Vector元素存储到的数组缓冲区。
protected Object[] elementData;
//Vector包含的元素数量
protected int elementCount;
//扩容时的增长系数
protected int capacityIncrement;

2.3.3 构造方法

Vector 的构造方法有以下三种:

  • 创建一个初始容量为 initialCapacity 空列表,并指定 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;
    }
    
  • 创建一个初始容量为 initialCapacity 的列表

    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    
  • 创建一个初始容量为10的列表

    public Vector() {
        this(10);
    }
    
  • 与 List 一样,创建一个包含指定 collection 的元素的列表

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

2.3.4 常用方法

  1. 增加元素
  • add(E e);:添加一个元素到列表的末尾

    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    
    • e 是传入的参数,elementData 是当前的数组,size 是当前数组的长度
    • 数组进行扩容,增加一个空间
    • 扩容完成继续执行,将元素 e 存放在第 size+1 个位置
    • 存放完成后数组长度+1
    • 与 ArrayList 类似,但是使用了 synchronized 进行同步。
  • 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++;
    }
    
    • 判断 index 是否超出数组的范围,是则抛出异常
    • 如果元素插入后的长度大于当前数组的长度,则进行扩容
    • 使用本地方法 arraycopy 对数组进行移动
    • 在 index 位置插入传入的元素
  1. 删除元素
  • removeElementAt(int index):删除index处的值

    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 是否有效,否则抛出异常
    • 变量 j ,判断 index 是否为最后一个元素,如果是,则直接将最后一个元素置为空
    • 如果不是,则调用本地方法进行数组的移动
    • 移动之后,最后一位还未删除,因此需要将最后一位置为空
  • removeElement(Object obj):删除列表中第一个出现o的元素

    public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
    }
    
    • 执行 indexof 方法,找到待删除元素的位置
    • 如果找到待删除元素的位置,则使用 removeElementAt 方法进行删除
    • 如果没有找到,则返回 false
  • removeAllElements():删除所有

    public synchronized void removeAllElements() {
        modCount++;
        // Let gc do its work
        for (int i = 0; i < elementCount; i++)
            elementData[i] = null;
        elementCount = 0;
    }
    
    • 遍历整个数组,将整个数组元素置为空
  1. 查询元素
  • indexOf(Object o):从头遍历,找到 obj 的下标,没有则返回 -1

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

    从 index 下标开始,寻找元素 o ,如果找到则返回下标,否则返回 -1

    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;
    }
    
  1. 修改元素
  • setElementAt(E obj, int index):将index处的值设为obj

    public synchronized void setElementAt(E obj, int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        elementData[index] = obj;
    }
    
    • 判断index是否有效,无效则抛出异常
    • 如果有效,则将元素赋值给数组的 index 位置
  1. 其他方法
  • size():返回元素的个数

  • setSize(int newsize):重新定义 Vector 的大小,如果 newsize 比原列表的长度小,则多余部分元素会丢失

    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;
    }
    
    • 如果 Vector 新定义的长大于原数组的长度,则进行扩容
    • 将原数组的值赋值给新数组
    • 如果新长度小于原数组的长度,则将超出的部分置为空
    • 并将新数组赋给原数组

2.3.5 扩容机制

Vector 的构造函数可以传入 capacityIncrement 参数,它的作用是在扩容时使容量 capacity 增长 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;
}

如果这个参数的值小于等于 0,扩容时每次都令 capacity 为原来的两倍。

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

调用没有 capacityIncrement 的构造函数时,capacityIncrement 值被设置为 0,也就是说默认情况下 Vector 每次扩容时容量都会翻倍。

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

public Vector() {
    this(10);
}

2.3.6 与 ArrayList 的比较

  • Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序员自己来控制;
  • Vector 每次扩容请求其大小的 2 倍(也可以通过构造函数设置增长的容量),而 ArrayList 是 1.5 倍。

2.3.7 替代方案

可以使用 Collections.synchronizedList(); 得到一个线程安全的 ArrayList。

List<String> list = new ArrayList<>();
List<String> synList = Collections.synchronizedList(list);

也可以使用 concurrent 并发包下的 CopyOnWriteArrayList 类。

List<String> list = new CopyOnWriteArrayList<>();

2.4 Stack

2.4.1 类结构

public class Stack<E> extends Vector<E> {}
  • 通过继承Vector类,Stack类可以实现Vertor的功能。
  • 在Java中Stack类表示后进先出(LIFO)对象堆栈
  • 栈是一种非常常见的数据结构,它采用典型的先进后出的操作方式完成的。
  • Stack仅有一个构造方法,拓展五个实现方法

2.4.2 常用方法

  1. empty():测试堆栈是否为空

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

    判断长度是否为0

  2. peek():查看堆栈顶部的对象,但不从堆栈中移除它

    public synchronized E peek() {
        int len = size();
        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }
    

    返回Vector中的最后一个元素

  3. pop():移除堆栈顶部的对象,并作为此函数的值返回该对象

    public synchronized E pop() {
        E       obj;
        int     len = size();
        obj = peek();
        removeElementAt(len - 1);
        return obj;
    }
    

    保存堆栈顶部的对象
    移除Vector中的最后一个元素

  4. push(E item):把元素压入堆栈顶部

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

    在Vector尾部添加元素item

  5. search(Object o):返回对象在堆栈中的位置,以 1 为基数

    public synchronized int search(Object o) {
        int i = lastIndexOf(o);
        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }
    

    从Vector尾部查找,再用长度减去位置,得到从堆栈底部数起的位置

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值