为什么JDK建议使用ArrayDeque实现栈

首先,先说为什么不建议使用Stack这个实现类:

https://www.xttblog.com/?p=3416

前面我已经写过一篇关于 Stack(栈) 的文章了《 吃多了拉就是队列,吃多了吐就是栈 》。鉴于网上关于 Stack 的文章众多,但大多都是偏介绍的。并且 Stack(栈)在 Java 中广泛存在,所以,我想再研究研究它。

我们先来看一下栈的应用吧。

  • 符号匹配
  • 中缀表达式转换为后缀表达式
  • 计算后缀表达式
  • 实现函数的嵌套调用
  • HTML 和 XML 文件中的标签匹配
  • 网页浏览器中已访问页面的历史记录
  • Java 中的程序计数栈、堆栈

除了这些,Java 中还有非常多的地方使用到了栈。

栈是一种数据结构,所以它使用的非常普遍。面试中遇到它的可能性也比较大,所以还是很有必要掌握的。

java.util.Stack 类 Java 官方已经不在建议使用了。现在官方推荐使用 java.util.Deque。类似下面的用法:

Deque<Integer> stack = new ArrayDeque<Integer>();

那么 Java 为什么不推荐使用 Stack 呢?为什么 Stack 被废弃了呢?

很简单,因为 Stack 是 JDK 1.0 的产物。它继承自 Vector,Vector 都不被推荐使用了,你说 Stack 还会被推荐吗?

当初 JDK1.0 在开发时,可能为了快速的推出一些基本的数据结构操作,所以推出了一些比较粗糙的类。比如,Vector、Stack、Hashtable 等。这些类中的一些方法加上了 synchronized 关键字,容易给一些初级程序员在使用上造成一些误解!而且在之前的几个版本中,性能还不怎么好。

基于 Vector 实现的栈 Stack。底层实际上还是数组,所以还是存在需要扩容。Vector 是由数组实现的集合类,他包含了大量集合处理的方法。而 Stack 之所以继承 Vector,是为了复用 Vector 中的方法,来实现进栈(push)、出栈 (pop) 等操作。这里就是 Stack 设计不好的地方,既然只是为了实现栈,不用链表来单独实现,而是为了复用简单的方法而迫使它继承 Vector,Stack 和 Vector 本来是毫无关系的。这使得 Stack 在基于数组实现上效率受影响,另外因为继承 Vector 类,Stack 可以复用 Vector 大量方法,这使得 Stack 在设计上不严谨。

基于上一篇文章中,我们对栈存在的一个基本认识,下面我们使用 LinkedList 自己实现一个栈。

package com.xttblog;
import java.util.LinkedList;
public class Stack<E> {
	LinkedList<E> list;
	public Stack(){
        list = new LinkedList();
    }
    public E pop(){
        return list.removeLast();
    }
    public void push(E o){
        list.add(o);
    }
    public E getTop(){
        return list.getLast();
    }
    public boolean isEmpty(){
        return list.size()==0;
    }
    public int size(){
        return list.size();
    }
}

img

栈的最基本的特征是 LIFO(Last In First Out),因此栈又被称为后进先出的线性表。所以上面采用 LinkedList 实现的栈看起来也非常的简单。虽然简单,但我们并不需要重复的轮子。Java 提供了 Deuqe。Deque 是继承自 Queue,而 Stack 是继承自 Vector。Java 中的 Deuqe,即 “double ended queue” 的缩写,是 Java 中的双端队列集合类型。Deque 具备普通队列 FIFO 的功能,同时它也具备了 Stack 的 LIFO 功能,并且保留了 push 和 pop 函数,所以使用起来应该是一点障碍都没有。

ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque 可以作为栈来使用,效率要高于 Stack。ArrayDeque 也可以作为队列来使用,效率相较于基于双向链表的 LinkedList 也要更好一些。注意,ArrayDeque 不支持为 null 的元素。


之后,再说为什么建议使用ArrayDeque而不是LinkedList:

https://stackoverflow.com/questions/6163166/why-is-arraydeque-better-than-linkedlist

  1. 链表比数组花费更多空间
  2. 链表的随机访问性质比数组差(虽然这个对栈来说问题不大)
  3. 链表的每次插入和删除都涉及到一个节点对象的创建和弃用,非常低效和浪费空间,而动态数组几乎是0花费的(数组充满时重新拷贝除外)
  4. 链表是非连续的,访问时候不能充分利用cpu cache

个人觉得第三点最重要

所以无论是栈还是队列,JDK都是建议使用ArrayDeque而不是LinkedList实现,ArrayDeque比较复杂的一点就是需要指定初始大小,当然你不指定也行。但是它的效率确实是比LinkedList高的

posted @ 2022-07-22 09:15  Dazzling!  阅读(157)  评论(0编辑  收藏  举报