为什么质疑Java中的Stack

java

问题由来

    曾经遇到过一条面试题,“Java中的Stack是通过Vector来实现的,这种设计被认为是不良的设计,说说你的看法?”

解析Java中的Stack

    众所周知Stack(栈)是一种先进后出的数据结构。当中有两个重要的方法:push(进栈)和pop(出栈)。

几乎所有语言在实现栈时,都会实现这两个方法,进栈和出栈。而栈这种数据结构在多数时候用来插入和删除元素(进栈则是在顶部插入元素,出栈则是从顶部删除元素),较少情况会用来查找元素。所以从实现方式上,大多是以链表方式实现而非数值方式实现(在插入删除方法上,链表效率优于数组效率)。

反观Java中的Stack,查看源代码:

   1:  public class Stack<E> extends Vector<E> {

   2:      /**

   3:       * Creates an empty Stack.

   4:       */

   5:      public Stack() {

   6:      }

   7:   

   8:      /**

   9:       * Pushes an item onto the top of this stack. This has exactly

  10:       * the same effect as:

  11:       * <blockquote><pre>

  12:       * addElement(item)</pre></blockquote>

  13:       *

  14:       * @param   item   the item to be pushed onto this stack.

  15:       * @return  the <code>item</code> argument.

  16:       * @see     java.util.Vector#addElement

  17:       */

  18:      public E push(E item) {

  19:      addElement(item);

  20:   

  21:      return item;

  22:      }

  23:   

  24:      /**

  25:       * Removes the object at the top of this stack and returns that

  26:       * object as the value of this function.

  27:       *

  28:       * @return     The object at the top of this stack (the last item

  29:       *             of the <tt>Vector</tt> object).

  30:       * @exception  EmptyStackException  if this stack is empty.

  31:       */

  32:      public synchronized E pop() {

  33:      E    obj;

  34:      int    len = size();

  35:   

  36:      obj = peek();

  37:      removeElementAt(len - 1);

  38:   

  39:      return obj;

  40:      }

  41:   

  42:      /**

  43:       * Looks at the object at the top of this stack without removing it

  44:       * from the stack.

  45:       *

  46:       * @return     the object at the top of this stack (the last item

  47:       *             of the <tt>Vector</tt> object).

  48:       * @exception  EmptyStackException  if this stack is empty.

  49:       */

  50:      public synchronized E peek() {

  51:      int    len = size();

  52:   

  53:      if (len == 0)

  54:          throw new EmptyStackException();

  55:      return elementAt(len - 1);

  56:      }

  57:   

  58:      /**

  59:       * Tests if this stack is empty.

  60:       *

  61:       * @return  <code>true</code> if and only if this stack contains

  62:       *          no items; <code>false</code> otherwise.

  63:       */

  64:      public boolean empty() {

  65:      return size() == 0;

  66:      }

  67:   

  68:      /**

  69:       * Returns the 1-based position where an object is on this stack.

  70:       * If the object <tt>o</tt> occurs as an item in this stack, this

  71:       * method returns the distance from the top of the stack of the

  72:       * occurrence nearest the top of the stack; the topmost item on the

  73:       * stack is considered to be at distance <tt>1</tt>. The <tt>equals</tt>

  74:       * method is used to compare <tt>o</tt> to the

  75:       * items in this stack.

  76:       *

  77:       * @param   o   the desired object.

  78:       * @return  the 1-based position from the top of the stack where

  79:       *          the object is located; the return value <code>-1</code>

  80:       *          indicates that the object is not on the stack.

  81:       */

  82:      public synchronized int search(Object o) {

  83:      int i = lastIndexOf(o);

  84:   

  85:      if (i >= 0) {

  86:          return size() - i;

  87:      }

  88:      return -1;

  89:      }

  90:   

  91:      /** use serialVersionUID from JDK 1.0.2 for interoperability */

  92:      private static final long serialVersionUID = 1224463164541339165L;

可以看到Stack继承Vector,而Vector是由数组实现的集合类,他包含了大量集合处理的方法。而Stack之所以继承Vector,是为了复用Vector中的方法,来实现进栈(push)、出栈(pop)等操作。

这里就是质疑Stack的地方,既然只是为了实现栈,为什么不用链表来单独实现,只是为了复用简单的方法而迫使它继承Vector(Stack和Vector本来是毫无关系的)。这使得Stack在基于数组实现上效率受影响,另外因为继承Vector类,Stack可以复用Vector大量方法,这使得Stack在设计上不严谨,当我们看到Vector中的:

   1:  public void add(int index, E element) {

   2:          insertElementAt(element, index);

   3:      }

可以在指定位置添加元素,这与Stack 的设计理念相冲突(栈只能在栈顶添加或删除元素)。

所以Java中的Stack实现确实值得商榷。

使用链表模拟Stack

一个单纯的栈,其实可以只包含以下方法:

boolean empty()
测试堆栈是否为空。 
T peek()


查看堆栈顶部的对象,但不从堆栈中移除它。 
T pop()


移除堆栈顶部的对象,并作为此函数的值返回该对象。 
void push(T item)


把项压入堆栈顶部。

我们使用链表来模拟一个栈:

   1:  public class Stack<T> {

   2:      private Node<T> top;

   3:   

   4:      /**

   5:       * 返回栈顶元素

   6:       * 

   7:       * @return

   8:       */

   9:      public T peek() {

  10:          return top.getData();

  11:      }

  12:   

  13:      /**

  14:       * 从栈中弹出项

  15:       * @return

  16:       */

  17:      public T pop() {

  18:          T data = null;

  19:          if (top != null) {

  20:              data = top.getData();

  21:              top = top.getNextNode();

  22:          }

  23:          return data;

  24:      }

  25:   

  26:      /**

  27:       * 向栈内压入项

  28:       * 

  29:       * @param data

  30:       */

  31:      public void push(T data) {

  32:          Node<T> node = new Node<T>();

  33:          node.setData(data);

  34:          if (top == null) {

  35:              top = node;

  36:          } else {

  37:              node.setNextNode(top);

  38:              top.setPreNode(node);

  39:              top = node;

  40:          }

  41:      }

  42:   

  43:      /**

  44:       * 是否是空栈

  45:       * 

  46:       * @return

  47:       */

  48:      public boolean isEmpty() {

  49:          return top == null;

  50:      }

  51:  }

   1:  public class Node<T> {

   2:      private T data;

   3:      // 前驱节点

   4:      private Node<T> preNode;

   5:      // 后继节点

   6:      private Node<T> nextNode;

   7:   

   8:      public T getData() {

   9:          return data;

  10:      }

  11:   

  12:      public void setData(T data) {

  13:          this.data = data;

  14:      }

  15:   

  16:      public Node<T> getPreNode() {

  17:          return preNode;

  18:      }

  19:   

  20:      public void setPreNode(Node<T> preNode) {

  21:          this.preNode = preNode;

  22:      }

  23:   

  24:      public Node<T> getNextNode() {

  25:          return nextNode;

  26:      }

  27:   

  28:      public void setNextNode(Node<T> nextNode) {

  29:          this.nextNode = nextNode;

  30:      }

  31:   

  32:  }

编写一个main测试:

   1:  /**

   2:       * @param args

   3:       */

   4:      public static void main(String[] args) {

   5:          Stack<Integer> stack = new Stack<Integer>();

   6:          stack.push(1);

   7:          stack.push(2);

   8:          System.out.println(stack.peek());

   9:          stack.push(3);

  10:          stack.push(4);

  11:          System.out.println(stack.peek());

  12:          stack.push(5);

  13:          stack.push(6);

  14:          System.out.println(stack.pop());

  15:          System.out.println(stack.peek());

  16:      }

以上是 为什么质疑Java中的Stack 的全部内容, 来源链接: utcz.com/z/390869.html

回到顶部