Java集合—Stack的源码深度解析以及应用介绍

  本文对Java中的Stack集合的源码进行了深度解析,包括各种方法的底层实现,并且给出了Stack的使用建议。

1 Stack的概述

public class Stack< E >
  extends Vector< E >

  Stack,来自于JDK1.0 的古老集合类,底层是数组结构,元素可重复,有序(存放顺序),支持下标索引访问,允许null元素。
  Stack类继承了Vector,所有方法的实现都是同步的,方法采用了synchronized修饰,数据安全,效率低!。
  Stack针对Vector扩展了五个API方法,主要被用来实现为先进后出(FILO)的栈。

2 Stack的源码解析

  由于Stack继承了Vector类,因此继承了Vector类的属性和方法,同时他扩展了Vector类的方法,即新增了五个方法,用于实现先进后出的栈结构。关于Vector集合的介绍,可以看这篇文章:Java的Vector集合源码深度解析以及应用介绍
  Stack的将底层数组的尾部看成了栈顶,因此入栈时将元素加在尾部,出栈时直接移除尾部的元素,比较简单,不会导致大量元素位置移动!

2.1 构造器

  仅有一个自己的构造器!用于创建一个空堆栈。

public Stack() {
}

2.2 API方法

  这里讲解相比于Vector新增的将用于实现栈的五个方法!

2.2.1. public E push(E item)

  压栈:将新元素放在栈顶的位置。新加入的元素把原来的元素往下“压”,默认最后加入的元素在栈顶。

/**
 * 压栈(入栈),在内部实现是将元素放在内部数组的末尾
 * @param item 添加的元素
 * @return 被添加的元素
 */
public E push(E item) {
    //调用了父类的addElement方法,将元素放在内部数组的末尾
    addElement(item);
    //返回被添加的元素
    return item;
}

2.2.2 public E peek()

  获得栈顶元素,但是不移除。如果栈为空,将抛出EmptyStackException异常。

/**
 * 获取栈顶元素但不移除
 * @return 栈顶元素
 */
public synchronized E peek() {
    //获取元素数量
    int     len = size();
    //如果数量等于0则说明是空栈,抛出异常
    if (len == 0)
        throw new EmptyStackException();
    //返回内部数组的尾部元素
    return elementAt(len - 1);
}

2.2.3 public E pop()

  获得栈顶,并移除(弹栈)。如果栈为空,将抛出EmptyStackException异常。

/**
 * 获取栈顶元素并不移除
 * @return 栈顶元素
 */
public synchronized E pop() {
    E       obj;
    //获取元素数量
    int     len = size();
    //获取栈顶元素但不移除
    obj = peek();
    //移除底层数组尾部元素
    removeElementAt(len - 1);
    //返回被出栈的元素
    return obj;
}

2.2.4 public boolean empty()

  判断栈容器是否为空栈。

public boolean empty() {
    //很简单,直接看元素数量是否等于0
    return size() == 0;
}

2.2.5 public int search(Object o)

  搜索o在栈空间当中的位置:以1为基础,从栈顶开始计算,返回值为-1时表示此对象不在堆栈中。底层使用 equals 方法比较 o 与堆栈中的项。

/**
 * 搜索o在栈空间当中的位置:以1为基础,从栈顶开始计算,返回值为-1时表示此对象不在堆栈中。底层使用 equals 方法比较 o 与堆栈中的项。
 * @param o 搜索的元素
 * @return 位置
 */
public synchronized int search(Object o) {
    //从尾部倒序向头部搜索索引位置,返回第一个被找到的元素的索引,没找到返回-1
    int i = lastIndexOf(o);
    //如果位置i大于等于0,则返回size-i;
    //即如果size=5,i=4,那么该元素在"栈"结构中的位置因该是1,即以1为基础,栈顶元素位置就是1
    if (i >= 0) {
        return size() - i;
    }
    //没找到就返回-1
    return -1;
}

2.2.6 案例

public class StackDemo01 {

    public static void main(String[] args) {
        Stack<String> s = new Stack<>();
        s.push("a");
        s.push("b");
        s.push("c");
        s.push("d");
        while (!s.empty()) {
            String pop = s.pop();
            System.out.print(pop + " ");
        }
    }
}

3 总结

  Stack类的实现还是很简单的,相比父类Vector就是多了五个方法。但是由于来自于JDK1.0,那时候的集合都很原始并且使用Synchronized修饰,性能可能不是很好,并且底层实现比较粗糙!
  在JDK1.6的时候,添加了集合类ArrayDeque,该类实现了Deque接口,即作为一个“双端队列”,可以被用来实现Stack(栈)或者Queue(队列)。
  ArrayDeque内部是采用可变数组实现的双端队列,采用两个外部引用来保持队列头结点和尾节点的访问,同时删除队列头尾元素时不会移动其他元素,而是移动引用的位置,即形成一个环形队列,能够复用数组空间(允许队头索引比队尾索引值更大),相比于另外一个使用链表实现的双端队列LinkedList综合效率更好(LinkedList介绍:Java的LinkedList集合源码深度解析以及应用介绍)!同时,如果用于实现栈,那么相比于Stack的综合效率同样更好!不过ArrayDeque不支持null元素,并且不是线程安全的!
  因此,JDK推荐我们一般使用ArrayDeque来替代Stack实现栈!
  同时如果你真的想了解JVM的栈空间的工作过程,可以看看这篇文章:Java的JVM运行时栈结构和方法调用详解,看完你会发现为什么上面的集合可以用来模拟“栈空间”了,因为方法在栈空间中的调用也是“先进后出”的!
  我们后续将会介绍的更多集合,比如TreeMap、HashMap,LinkedHashMap、HashSet、TreeSet、ArrayDeque等基本集合以及JUC包中的高级并发集合。如果想学习集合源码的关注我的专栏更新!

如果有什么不懂或者需要交流,可以留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

猜你喜欢

转载自blog.csdn.net/weixin_43767015/article/details/106559947