【源码】Vector、Stack源码解析

注:以下源码基于jdk1.7.0_11

Vector算是一个历史遗留下来的类,现在已基本被ArrayList取代。本文出于学习的目的来分析下这个类。

从图上可以看出Vector和ArrayList同样都直接继承于AbstractList,说明这两者功能上还是很相像的,事实也正是如此。

下面我们依然通过源码的方式解读Vector这个类。

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

Vector的继承关系和ArrayList保持一致。但是成员变量确不太一样:

  protected Object[] elementData;//使用数组存储元素
  protected int elementCount;//元素个数
  protected int capacityIncrement;//扩容增量
   /** use serialVersionUID from JDK 1.0.2 for interoperability */
  private static final long serialVersionUID = -2767605614048989439L;

Vector多了一个capacityIncrement这个变量,并且需要在构造器中指定这个值(默认为0,可以手动指定):

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

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

    public Vector() {//默认大小为10
        this(10);
    }
    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);
    }

除此之外,Vector的初始容量与ArrayList一样,都是10。

下面来看下Vector的扩容机制

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

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    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);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

但从方法命名上来看,跟ArrayList还是很类似的,但是两者的grow方法有点小区别:

Vector的扩容是基于capacityIncrement的,也就是所谓的扩容增量,如果该值不为0,那么每次扩容后的大小就是在原始容量加上扩容增量。如果未设置capacityIncrement,那么直接扩容为原来的两倍。

当然,前提是扩容后大小得大于等于所需要的最小容量minCapacity且不能超过MAX_ARRAY_SIZE,同时还要防止溢出(会抛出异常)。顺便复习下ArrayList的扩容机制:ArrayList每次扩容后大小都为原来的1.5倍

接下来,我们观察一些方法:

 public synchronized void trimToSize() {
        modCount++;
        int oldCapacity = elementData.length;
        if (elementCount < oldCapacity) {
            elementData = Arrays.copyOf(elementData, elementCount);
        }
    }
   public synchronized int size() {
        return elementCount;
    }
 public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
   public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
    }

可以发现,这些方法都加上了synchronized关键字,也就是说Vector是一个线程安全的类

另外,Vector也是支持null元素的。

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

Vector除了ListIterator和iterator两种迭代方式之外,还有独特的迭代方式,那就是elements方法:

  public Enumeration<E> elements() {
        return new Enumeration<E>() {//匿名内部类方式构造一个Enumeration对象。
            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");
            }
        };
    }

这个方法通过匿名内部类的方式构造一个Enumeration对象,并实现了hasMoreElements和nextElement方法,类似迭代器的hasNext和next方法。

以上就是Vector的内容,下面再看看Stack类:

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;
    }
    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = 1224463164541339165L;
}

总结:

1.Vector是线程安全的类而ArrayList不是;

2.Vector的扩容基于扩容增量,若扩容增量不为0,每次扩容的大小都为原始容量加上扩容增量,若扩容增量为0,也就是默认的情况下,扩容大小为元素容量的两倍;

3.Vector默认大小为10;

4.Vector支持null(ArrayList也可以);

5.Vector除了可以使用listIterator和Iterator遍历集合,也可以使用Enumeration的方式遍历;

6.Stack类继承了Vector,并封装了栈的操作。同时也是一个线程安全的类。

7.从性能上看,推荐使用ArrayList和LinkedList而不要使用Vector和Stack。

【源码】Vector、Stack源码解析

时间: 2024-08-27 17:04:56

【源码】Vector、Stack源码解析的相关文章

Stack源码解析

Stack介绍: 堆栈(Stack)是一个元素序列.盾战中唯一能被删除.访问或修改的元素是最近插入的元素.这个元素就是位于堆栈顶部的那个元素. 举例来说,自助餐厅的盘子架就是一个由盘子构成的堆栈.增加和移除操作只能在顶部进行.为了把它重新摆放,刚刚放到架子上的盘子将在下一次被移动.这种堆栈属性的定义有时简称为"后进先出",或者LIFO.和这种观点相对应,我们把插入称为压栈(push),删除称为弹栈(pop).为了逗以p字母开头,我们把得到栈顶元素的操作称为浏览(peek). publi

Java Stack源码分析

Stack简介 Stack是栈.它的特性是:先进后出(FILO, First In Last Out).java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现的,这就意味着,Stack也是通过数组实现的,而非链表.当然,我们也可以将LinkedList当作栈来使用.Stack的继承关系 java.lang.Object ? java.util.AbstractCollection<E> ? java.util.AbstractList<E>

STL 之 stack 源码剖析

G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_stack.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fe

Chromium源码--视频播放流程分析(WebMediaPlayerImpl解析)

转载请注明出处:http://www.cnblogs.com/fangkm/p/3797278.html 承接上一篇文章.媒体播放,需要指定一个源文件,html5用URL格式来指定视频源文件地址,可以是http链接,也可以使本地源文件(不能直接指定,需要借助blob二进制类型).播放网络文件比播放本地文件多了个下载流程, 所以下面直接分析网络文件的播放流程,本地文件的播放流程也就清楚了.首先分析下网络视频资源的加载流程,相关结构图如下: WebMediaPlayerImpl类有一成员Buffer

STL源码--vector(一)

一.vector的特性 vector其中一个特点:内存空间只会增长,不会减小,援引C++ Primer:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储.设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间.拷贝元素.撤销旧空间,这样性能难以接受.因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些.就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每

Spring源码情操陶冶#task:scheduled-tasks解析器

承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 Spring中的定时任务基本配置样例如下 <!--create schedule thread pool--> <task:scheduler id="baseScheduler" pool-size="5"></task:scheduler

Spring源码情操陶冶-tx:advice解析器

承接Spring源码情操陶冶-自定义节点的解析.本节关于事务进行简单的解析 spring配置文件样例 简单的事务配置,对save/delete开头的方法加事务,get/find开头的设置为不加事务只读模式 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*"

Dubbo 源码分析 - 服务导出全过程解析

1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一是前置工作,主要用于检查参数,组装 URL.第二是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程.第三是向注册中心注册服务,用于服务发现.本篇文章将会对这三个部分代码进行详细的分析,在分析之前,我们先来了解一下服务的导出过程. Dubbo 支持两种服务导出方式,

曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中得到了什么(util命名空间)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de